From 8b79ce8bc22d224b447eca97b9d9ff07a49f9f72 Mon Sep 17 00:00:00 2001 From: Guilherme Avila Date: Mon, 13 Jan 2020 17:22:55 -0300 Subject: [PATCH 1/2] EXRLoader: add support for DWAA/B compression --- examples/js/loaders/EXRLoader.js | 687 +++++++++++++++++++++++++++++- examples/jsm/loaders/EXRLoader.js | 687 +++++++++++++++++++++++++++++- 2 files changed, 1368 insertions(+), 6 deletions(-) diff --git a/examples/js/loaders/EXRLoader.js b/examples/js/loaders/EXRLoader.js index 8448d4a606c5ab..8eb9e884d905cd 100644 --- a/examples/js/loaders/EXRLoader.js +++ b/examples/js/loaders/EXRLoader.js @@ -108,6 +108,15 @@ THREE.EXRLoader.prototype = Object.assign( Object.create( THREE.DataTextureLoade const INT16_SIZE = 2; const INT8_SIZE = 1; + const STATIC_HUFFMAN = 0; + const DEFLATE = 1; + + const UNKNOWN = 0; + const LOSSY_DCT = 1; + const RLE = 2; + + const logBase = Math.pow( 2.7182818, 2.2 ); + function reverseLutFromBitmap( bitmap, lut ) { var k = 0; @@ -736,7 +745,7 @@ THREE.EXRLoader.prototype = Object.assign( Object.create( THREE.DataTextureLoade var count = - l; size -= count + 1; - + for ( var i = 0; i < count; i ++ ) { out.push( reader.getUint8( p ++ ) ); @@ -748,7 +757,7 @@ THREE.EXRLoader.prototype = Object.assign( Object.create( THREE.DataTextureLoade var count = l; size -= 2; - + var value = reader.getUint8( p ++ ); for ( var i = 0; i < count + 1; i ++ ) { @@ -757,7 +766,6 @@ THREE.EXRLoader.prototype = Object.assign( Object.create( THREE.DataTextureLoade } - } } @@ -766,6 +774,400 @@ THREE.EXRLoader.prototype = Object.assign( Object.create( THREE.DataTextureLoade } + function lossyDctDecode( cscSet, rowPtrs, channelData, acBuffer, dcBuffer, outBuffer ) { + + var dataView = new DataView( outBuffer.buffer ); + + var width = channelData[ cscSet.idx[ 0 ] ].width; + var height = channelData[ cscSet.idx[ 0 ] ].height; + + var numComp = 3; + + var numFullBlocksX = Math.floor( width / 8.0 ); + var numBlocksX = Math.ceil( width / 8.0 ); + var numBlocksY = Math.ceil( height / 8.0 ); + var leftoverX = width - ( numBlocksX - 1 ) * 8; + var leftoverY = height - ( numBlocksY - 1 ) * 8; + + var currAcComp = { value: 0 }; + var currDcComp = new Array( numComp ); + var dctData = new Array( numComp ); + var halfZigBlock = new Array( numComp ); + var rowBlock = new Array( numComp ); + var rowOffsets = new Array( numComp ); + + for ( let comp = 0; comp < numComp; ++ comp ) { + + rowOffsets[ comp ] = rowPtrs[ cscSet.idx[ comp ] ]; + currDcComp[ comp ] = ( comp < 1 ) ? 0 : currDcComp[ comp - 1 ] + numBlocksX * numBlocksY; + dctData[ comp ] = new Float32Array( 64 ); + halfZigBlock[ comp ] = new Uint16Array( 64 ); + rowBlock[ comp ] = new Uint16Array( numBlocksX * 64 ); + + } + + for ( let blocky = 0; blocky < numBlocksY; ++ blocky ) { + + var maxY = 8; + + if ( blocky == numBlocksY - 1 ) + maxY = leftoverY; + + var maxX = 8; + + for ( let blockx = 0; blockx < numBlocksX; ++ blockx ) { + + if ( blockx == numBlocksX - 1 ) + maxX = leftoverX; + + for ( let comp = 0; comp < numComp; ++ comp ) { + + halfZigBlock[ comp ].fill( 0 ); + + // set block DC component + halfZigBlock[ comp ][ 0 ] = dcBuffer[ currDcComp[ comp ]++ ]; + // set block AC components + unRleAC( currAcComp, acBuffer, halfZigBlock[ comp ] ); + + // UnZigZag block to float + unZigZag( halfZigBlock[ comp ], dctData[ comp ] ); + // decode float dct + dctInverse( dctData[ comp ] ); + + } + + if ( numComp == 3 ) { + + csc709Inverse( dctData ); + + } + + for ( let comp = 0; comp < numComp; ++ comp ) { + + convertToHalf( dctData[ comp ], rowBlock[ comp ], blockx * 64 ); + + } + + } // blockx + + let offset = 0; + + for ( let comp = 0; comp < numComp; ++ comp ) { + + let type = channelData[ cscSet.idx[ comp ] ].type; + + for ( let y = 8 * blocky; y < 8 * blocky + maxY; ++ y ) { + + offset = rowOffsets[ comp ][ y ]; + + for ( let blockx = 0; blockx < numFullBlocksX; ++ blockx ) { + + let src = blockx * 64 + ( ( y & 0x7 ) * 8 ); + + dataView.setUint16( offset + 0 * INT16_SIZE * type, rowBlock[ comp ][ src + 0 ], true ); + dataView.setUint16( offset + 1 * INT16_SIZE * type, rowBlock[ comp ][ src + 1 ], true ); + dataView.setUint16( offset + 2 * INT16_SIZE * type, rowBlock[ comp ][ src + 2 ], true ); + dataView.setUint16( offset + 3 * INT16_SIZE * type, rowBlock[ comp ][ src + 3 ], true ); + + dataView.setUint16( offset + 4 * INT16_SIZE * type, rowBlock[ comp ][ src + 4 ], true ); + dataView.setUint16( offset + 5 * INT16_SIZE * type, rowBlock[ comp ][ src + 5 ], true ); + dataView.setUint16( offset + 6 * INT16_SIZE * type, rowBlock[ comp ][ src + 6 ], true ); + dataView.setUint16( offset + 7 * INT16_SIZE * type, rowBlock[ comp ][ src + 7 ], true ); + + offset += 8 * INT16_SIZE * type; + + } + + } + + // handle partial X blocks + if ( numFullBlocksX != numBlocksX ) { + + for ( let y = 8 * blocky; y < 8 * blocky + maxY; ++ y ) { + + let offset = rowOffsets[ comp ][ y ] + 8 * numFullBlocksX * INT16_SIZE * type; + let src = numFullBlocksX * 64 + ( ( y & 0x7 ) * 8 ); + + for ( let x = 0; x < maxX; ++ x ) { + + dataView.setUint16( offset + x * INT16_SIZE * type, rowBlock[ comp ][ src + x ], true ); + + } + + } + + } + + } // comp + + } // blocky + + var halfRow = new Uint16Array( width ); + var dataView = new DataView( outBuffer.buffer ); + + // convert channels back to float, if needed + for ( var comp = 0; comp < numComp; ++ comp ) { + + channelData[ cscSet.idx[ comp ] ].decoded = true; + var type = channelData[ cscSet.idx[ comp ] ].type; + + if ( channelData[ comp ].type != 2 ) continue; + + for ( var y = 0; y < height; ++ y ) { + + let offset = rowOffsets[ comp ][ y ]; + + for ( var x = 0; x < width; ++ x ) { + + halfRow[ x ] = dataView.getUint16( offset + x * INT16_SIZE * type, true ); + + } + + for ( var x = 0; x < width; ++ x ) { + + dataView.setFloat32( offset + x * INT16_SIZE * type, decodeFloat16( halfRow[ x ] ), true ); + + } + + } + + } + + } + + function unRleAC( currAcComp, acBuffer, halfZigBlock ) { + + var acValue; + var dctComp = 1; + + while ( dctComp < 64 ) { + + acValue = acBuffer[ currAcComp.value ]; + + if ( acValue == 0xff00 ) { + + dctComp = 64; + + } else if ( acValue >> 8 == 0xff ) { + + dctComp += acValue & 0xff; + + } else { + + halfZigBlock[ dctComp ] = acValue; + dctComp++; + + } + + currAcComp.value++; + + } + + } + + function unZigZag( src, dst ) { + + dst[0] = decodeFloat16( src[0] ); + dst[1] = decodeFloat16( src[1] ); + dst[2] = decodeFloat16( src[5] ); + dst[3] = decodeFloat16( src[6] ); + dst[4] = decodeFloat16( src[14] ); + dst[5] = decodeFloat16( src[15] ); + dst[6] = decodeFloat16( src[27] ); + dst[7] = decodeFloat16( src[28] ); + dst[8] = decodeFloat16( src[2] ); + dst[9] = decodeFloat16( src[4] ); + + dst[10] = decodeFloat16( src[7] ); + dst[11] = decodeFloat16( src[13] ); + dst[12] = decodeFloat16( src[16] ); + dst[13] = decodeFloat16( src[26] ); + dst[14] = decodeFloat16( src[29] ); + dst[15] = decodeFloat16( src[42] ); + dst[16] = decodeFloat16( src[3] ); + dst[17] = decodeFloat16( src[8] ); + dst[18] = decodeFloat16( src[12] ); + dst[19] = decodeFloat16( src[17] ); + + dst[20] = decodeFloat16( src[25] ); + dst[21] = decodeFloat16( src[30] ); + dst[22] = decodeFloat16( src[41] ); + dst[23] = decodeFloat16( src[43] ); + dst[24] = decodeFloat16( src[9] ); + dst[25] = decodeFloat16( src[11] ); + dst[26] = decodeFloat16( src[18] ); + dst[27] = decodeFloat16( src[24] ); + dst[28] = decodeFloat16( src[31] ); + dst[29] = decodeFloat16( src[40] ); + + dst[30] = decodeFloat16( src[44] ); + dst[31] = decodeFloat16( src[53] ); + dst[32] = decodeFloat16( src[10] ); + dst[33] = decodeFloat16( src[19] ); + dst[34] = decodeFloat16( src[23] ); + dst[35] = decodeFloat16( src[32] ); + dst[36] = decodeFloat16( src[39] ); + dst[37] = decodeFloat16( src[45] ); + dst[38] = decodeFloat16( src[52] ); + dst[39] = decodeFloat16( src[54] ); + + dst[40] = decodeFloat16( src[20] ); + dst[41] = decodeFloat16( src[22] ); + dst[42] = decodeFloat16( src[33] ); + dst[43] = decodeFloat16( src[38] ); + dst[44] = decodeFloat16( src[46] ); + dst[45] = decodeFloat16( src[51] ); + dst[46] = decodeFloat16( src[55] ); + dst[47] = decodeFloat16( src[60] ); + dst[48] = decodeFloat16( src[21] ); + dst[49] = decodeFloat16( src[34] ); + + dst[50] = decodeFloat16( src[37] ); + dst[51] = decodeFloat16( src[47] ); + dst[52] = decodeFloat16( src[50] ); + dst[53] = decodeFloat16( src[56] ); + dst[54] = decodeFloat16( src[59] ); + dst[55] = decodeFloat16( src[61] ); + dst[56] = decodeFloat16( src[35] ); + dst[57] = decodeFloat16( src[36] ); + dst[58] = decodeFloat16( src[48] ); + dst[59] = decodeFloat16( src[49] ); + + dst[60] = decodeFloat16( src[57] ); + dst[61] = decodeFloat16( src[58] ); + dst[62] = decodeFloat16( src[62] ); + dst[63] = decodeFloat16( src[63] ); + + } + + function dctInverse( data ) { + + const a = .5 * Math.cos ( 3.14159 / 4.0 ); + const b = .5 * Math.cos ( 3.14159 / 16.0 ); + const c = .5 * Math.cos ( 3.14159 / 8.0 ); + const d = .5 * Math.cos ( 3.*3.14159 / 16.0 ); + const e = .5 * Math.cos ( 5.*3.14159 / 16.0 ); + const f = .5 * Math.cos ( 3.*3.14159 / 8.0 ); + const g = .5 * Math.cos ( 7.*3.14159 / 16.0 ); + + var alpha = new Array( 4 ); + var beta = new Array( 4 ); + var theta = new Array( 4 ); + var gamma = new Array( 4 ); + + for ( var row = 0; row < 8; ++ row ) { + + var rowPtr = row * 8; + + alpha[ 0 ] = c * data[ rowPtr + 2 ]; + alpha[ 1 ] = f * data[ rowPtr + 2 ]; + alpha[ 2 ] = c * data[ rowPtr + 6 ]; + alpha[ 3 ] = f * data[ rowPtr + 6 ]; + + beta[ 0 ] = b * data[ rowPtr + 1 ] + d * data[ rowPtr + 3 ] + e * data[ rowPtr + 5 ] + g * data[ rowPtr + 7 ]; + beta[ 1 ] = d * data[ rowPtr + 1 ] - g * data[ rowPtr + 3 ] - b * data[ rowPtr + 5 ] - e * data[ rowPtr + 7 ]; + beta[ 2 ] = e * data[ rowPtr + 1 ] - b * data[ rowPtr + 3 ] + g * data[ rowPtr + 5 ] + d * data[ rowPtr + 7 ]; + beta[ 3 ] = g * data[ rowPtr + 1 ] - e * data[ rowPtr + 3 ] + d * data[ rowPtr + 5 ] - b * data[ rowPtr + 7 ]; + + theta[ 0 ] = a * ( data[ rowPtr + 0 ] + data[ rowPtr + 4 ] ); + theta[ 3 ] = a * ( data[ rowPtr + 0 ] - data[ rowPtr + 4 ] ); + theta[ 1 ] = alpha[ 0 ] + alpha[ 3 ]; + theta[ 2 ] = alpha[ 1 ] - alpha[ 2 ]; + + gamma[ 0 ] = theta[ 0 ] + theta[ 1 ]; + gamma[ 1 ] = theta[ 3 ] + theta[ 2 ]; + gamma[ 2 ] = theta[ 3 ] - theta[ 2 ]; + gamma[ 3 ] = theta[ 0 ] - theta[ 1 ]; + + data[ rowPtr + 0 ] = gamma[ 0 ] + beta[ 0 ]; + data[ rowPtr + 1 ] = gamma[ 1 ] + beta[ 1 ]; + data[ rowPtr + 2 ] = gamma[ 2 ] + beta[ 2 ]; + data[ rowPtr + 3 ] = gamma[ 3 ] + beta[ 3 ]; + + data[ rowPtr + 4 ] = gamma[ 3 ] - beta[ 3 ]; + data[ rowPtr + 5 ] = gamma[ 2 ] - beta[ 2 ]; + data[ rowPtr + 6 ] = gamma[ 1 ] - beta[ 1 ]; + data[ rowPtr + 7 ] = gamma[ 0 ] - beta[ 0 ]; + + } + + for ( var column = 0; column < 8; ++ column ) { + + alpha[ 0 ] = c * data[ 16 + column ]; + alpha[ 1 ] = f * data[ 16 + column ]; + alpha[ 2 ] = c * data[ 48 + column ]; + alpha[ 3 ] = f * data[ 48 + column ]; + + beta[ 0 ] = b * data[ 8 + column ] + d * data[ 24 + column ] + e * data[ 40 + column ] + g * data[ 56 + column ]; + beta[ 1 ] = d * data[ 8 + column ] - g * data[ 24 + column ] - b * data[ 40 + column ] - e * data[ 56 + column ]; + beta[ 2 ] = e * data[ 8 + column ] - b * data[ 24 + column ] + g * data[ 40 + column ] + d * data[ 56 + column ]; + beta[ 3 ] = g * data [8 + column ] - e * data[ 24 + column ] + d * data[ 40 + column ] - b * data[ 56 + column ]; + + theta[ 0 ] = a * ( data[ column ] + data[ 32 + column ] ); + theta[ 3 ] = a * ( data[ column ] - data[ 32 + column ] ); + + theta[ 1 ] = alpha[ 0 ] + alpha[ 3 ]; + theta[ 2 ] = alpha[ 1 ] - alpha[ 2 ]; + + gamma[ 0 ] = theta[ 0 ] + theta[ 1 ]; + gamma[ 1 ] = theta[ 3 ] + theta[ 2 ]; + gamma[ 2 ] = theta[ 3 ] - theta[ 2 ]; + gamma[ 3 ] = theta[ 0 ] - theta[ 1 ]; + + data[ column ] = gamma[ 0 ] + beta[ 0 ]; + data[ 8 + column ] = gamma[ 1 ] + beta[ 1 ]; + data[ 16 + column ] = gamma[ 2 ] + beta[ 2 ]; + data[ 24 + column ] = gamma[ 3 ] + beta[ 3 ]; + + data[ 32 + column ] = gamma[ 3 ] - beta[ 3 ]; + data[ 40 + column ] = gamma[ 2 ] - beta[ 2 ]; + data[ 48 + column ] = gamma[ 1 ] - beta[ 1 ]; + data[ 56 + column ] = gamma[ 0 ] - beta[ 0 ]; + + } + + } + + function csc709Inverse( data ) { + + for ( var i = 0; i < 64; ++ i ) { + + var y = data[ 0 ][ i ]; + var cb = data[ 1 ][ i ]; + var cr = data[ 2 ][ i ]; + + data[ 0 ][ i ] = y + 1.5747 * cr; + data[ 1 ][ i ] = y - 0.1873 * cb - 0.4682 * cr; + data[ 2 ][ i ] = y + 1.8556 * cb; + + } + + } + + function convertToHalf( src, dst, idx ) { + + for ( var i = 0; i < 64; ++ i ) { + + dst[ idx + i ] = encodeFloat16( toLinear( src[ i ] ) ); + + } + + } + + function toLinear( float ) { + + if ( float <= 1 ) { + + return Math.sign( float ) * Math.pow( Math.abs( float ), 2.2 ); + + } else { + + return Math.sign( float ) * Math.pow( logBase, Math.abs( float ) - 1.0 ); + + } + + } + function uncompressRAW( info ) { return new DataView( info.array.buffer, info.offset.value, info.size ); @@ -911,6 +1313,223 @@ THREE.EXRLoader.prototype = Object.assign( Object.create( THREE.DataTextureLoade } + function uncompressDWA( info ) { + + var inDataView = info.viewer; + var inOffset = { value: info.offset.value }; + var outBuffer = new Uint8Array( info.width * info.lines * ( EXRHeader.channels.length * info.type * INT16_SIZE ) ); + + // Read compression header information + var dwaHeader = { + + version: parseInt64( inDataView, inOffset ), + unknownUncompressedSize: parseInt64( inDataView, inOffset ), + unknownCompressedSize: parseInt64( inDataView, inOffset ), + acCompressedSize: parseInt64( inDataView, inOffset ), + dcCompressedSize: parseInt64( inDataView, inOffset ), + rleCompressedSize: parseInt64( inDataView, inOffset ), + rleUncompressedSize: parseInt64( inDataView, inOffset ), + rleRawSize: parseInt64( inDataView, inOffset ), + totalAcUncompressedCount: parseInt64( inDataView, inOffset ), + totalDcUncompressedCount: parseInt64( inDataView, inOffset ), + acCompression: parseInt64( inDataView, inOffset ) + + } + + if ( dwaHeader.version < 2 ) + throw 'EXRLoader.parse: ' + EXRHeader.compression + ' version ' + dwaHeader.version + ' is unsupported'; + + // Read channel ruleset information + var channelRules = new Array(); + var ruleSize = parseUint16( inDataView, inOffset ) - INT16_SIZE; + + while ( ruleSize > 0 ) { + + var name = parseNullTerminatedString( inDataView.buffer, inOffset ); + var value = parseUint8( inDataView, inOffset ); + var compression = ( value >> 2 ) & 3; + var csc = ( value >> 4 ) - 1; + var index = new Int8Array( [ csc ] )[ 0 ]; + var type = parseUint8( inDataView, inOffset ); + + channelRules.push( { + name: name, + index: index, + type: type, + compression: compression, + } ); + + ruleSize -= name.length + 3; + + } + + // Classify channels + var channels = EXRHeader.channels; + var channelData = new Array( info.channels ); + + for ( var i = 0; i < info.channels; ++ i ) { + + var cd = channelData[ i ] = {}; + var channel = channels[ i ]; + + cd.name = channel.name; + cd.compression = UNKNOWN; + cd.decoded = false; + cd.type = channel.pixelType; + cd.pLinear = channel.pLinear; + cd.width = info.width; + cd.height = info.lines; + + } + + var cscSet = { + idx: new Array( 3 ) + }; + + for ( var offset = 0; offset < info.channels; ++ offset ) { + + var cd = channelData[ offset ]; + + for ( var i = 0; i < channelRules.length; ++ i ) { + + var rule = channelRules[ i ]; + + if ( cd.name == rule.name ) { + + cd.compression = rule.compression; + + if ( rule.index >= 0 ) { + + cscSet.idx[ rule.index ] = offset; + + } + + cd.offset = offset; + + } + + } + + } + + // Read DCT - AC component data + if ( dwaHeader.acCompressedSize > 0 ) { + + switch ( dwaHeader.acCompression ) { + + case STATIC_HUFFMAN: + + var acBuffer = new Uint16Array( dwaHeader.totalAcUncompressedCount ); + hufUncompress( info.array, inDataView, inOffset, dwaHeader.acCompressedSize, acBuffer, dwaHeader.totalAcUncompressedCount ); + break; + + case DEFLATE: + + var compressed = info.array.slice( inOffset.value, inOffset.value + dwaHeader.totalAcUncompressedCount ); + var inflate = new Zlib.Inflate( compressed, { resize: true, verify: true } ); + var acBuffer = new Uint16Array( inflate.decompress().buffer ); + inOffset.value += dwaHeader.totalAcUncompressedCount; + break; + + } + + + } + + // Read DCT - DC component data + if ( dwaHeader.dcCompressedSize > 0 ) { + + var zlibInfo = { + array: info.array, + offset: inOffset, + size: dwaHeader.dcCompressedSize + } + var dcBuffer = new Uint16Array( uncompressZIP( zlibInfo ).buffer ); + inOffset.value += dwaHeader.dcCompressedSize; + + } + + // Read RLE compressed data + if ( dwaHeader.rleRawSize > 0 ) { + + var compressed = info.array.slice( inOffset.value, inOffset.value + dwaHeader.rleCompressedSize ); + var inflate = new Zlib.Inflate( compressed, { resize: true, verify: true } ); + var rleBuffer = decodeRunLength( inflate.decompress().buffer ); + + inOffset.value += dwaHeader.rleCompressedSize; + + } + + // Prepare outbuffer data offset + var outBufferEnd = 0; + var rowOffsets = new Array( channelData.length ); + for ( var i = 0; i < rowOffsets.length; ++ i ) { + + rowOffsets[ i ] = new Array(); + + } + + for ( var y = 0; y < info.lines; ++ y ) { + + for ( var chan = 0; chan < channelData.length; ++ chan ) { + + rowOffsets[ chan ].push( outBufferEnd ); + outBufferEnd += channelData[ chan ].width * info.type * INT16_SIZE; + + } + + } + + // Lossy DCT decode RGB channels + lossyDctDecode( cscSet, rowOffsets, channelData, acBuffer, dcBuffer, outBuffer ); + + // Decode other channels + for ( var i = 0; i < channelData.length; ++ i ) { + + var cd = channelData[ i ]; + + if ( cd.decoded ) continue; + + switch ( cd.compression ) { + + case RLE: + + var row = 0; + var rleOffset = 0; + + for ( var y = 0; y < info.lines; ++ y ) { + + var rowOffsetBytes = rowOffsets[ i ][ row ]; + + for ( var x = 0; x < cd.width; ++ x ) { + + for ( var byte = 0; byte < INT16_SIZE * cd.type; ++ byte ) { + + outBuffer[ rowOffsetBytes++ ] = rleBuffer[ rleOffset + byte * cd.width * cd.height ]; + + } + + rleOffset++ + + } + + row++; + + } + + break; + + default: + throw 'EXRLoader.parse: unsupported channel compression'; + + } + + } + + return new DataView( outBuffer.buffer ); + + } + function parseNullTerminatedString( buffer, offset ) { var uintBuffer = new Uint8Array( buffer ); @@ -984,6 +1603,16 @@ THREE.EXRLoader.prototype = Object.assign( Object.create( THREE.DataTextureLoade } + function parseInt64( dataView, offset ) { + + var int = Number( dataView.getBigInt64( offset.value, true ) ); + + offset.value += ULONG_SIZE; + + return int; + + } + function parseFloat32( dataView, offset ) { var float = dataView.getFloat32( offset.value, true ); @@ -1012,6 +1641,46 @@ THREE.EXRLoader.prototype = Object.assign( Object.create( THREE.DataTextureLoade } + // https://stackoverflow.com/questions/32633585/how-do-you-convert-to-half-floats-in-javascript + var encodeFloat16 = ( function() { + + var floatView = new Float32Array(1); + var int32View = new Int32Array(floatView.buffer); + + return function toHalf( fval ) { + + floatView[ 0 ] = fval; + var fbits = int32View[ 0 ]; + var sign = ( fbits >> 16 ) & 0x8000; // sign only + var val = ( fbits & 0x7fffffff ) + 0x1000; // rounded value + + if ( val >= 0x47800000 ) { // might be or become NaN/Inf + if ( ( fbits & 0x7fffffff ) >= 0x47800000 ) { + // is or must become NaN/Inf + if( val < 0x7f800000 ) { // was value but too large + return sign | 0x7c00; // make it +/-Inf + } + return sign | 0x7c00 | // remains +/-Inf or NaN + ( fbits & 0x007fffff ) >> 13; // keep NaN (and Inf) bits + } + return sign | 0x7bff; // unrounded not quite Inf + } + if ( val >= 0x38800000 ) { // remains normalized value + return sign | val - 0x38000000 >> 13; // exp - 127 + 15 + } + if ( val < 0x33000000 ) { // too small for subnormal + return sign; // becomes +/-0 + } + val = ( fbits & 0x7fffffff ) >> 23; // tmp exp for subnormal calc + return sign | ( ( fbits & 0x7fffff | 0x800000 ) // add subnormal bit + + ( 0x800000 >>> val - 102 ) // round depending on cut off + >> 126 - val ); // div by 2^(1-(exp-127+15)) and >> 13 | exp= + + }; + + } () ); + + function parseUint16( dataView, offset ) { var Uint16 = dataView.getUint16( offset.value, true ); @@ -1245,6 +1914,18 @@ THREE.EXRLoader.prototype = Object.assign( Object.create( THREE.DataTextureLoade uncompress = uncompressPIZ; break; + case 'DWAA_COMPRESSION': + + scanlineBlockSize = 32; + uncompress = uncompressDWA; + break; + + case 'DWAB_COMPRESSION': + + scanlineBlockSize = 256; + uncompress = uncompressDWA; + break; + default: throw 'EXRLoader.parse: ' + EXRHeader.compression + ' is unsupported'; diff --git a/examples/jsm/loaders/EXRLoader.js b/examples/jsm/loaders/EXRLoader.js index cd44f96f44d9d5..0ac661468a2f44 100644 --- a/examples/jsm/loaders/EXRLoader.js +++ b/examples/jsm/loaders/EXRLoader.js @@ -119,6 +119,15 @@ EXRLoader.prototype = Object.assign( Object.create( DataTextureLoader.prototype const INT16_SIZE = 2; const INT8_SIZE = 1; + const STATIC_HUFFMAN = 0; + const DEFLATE = 1; + + const UNKNOWN = 0; + const LOSSY_DCT = 1; + const RLE = 2; + + const logBase = Math.pow( 2.7182818, 2.2 ); + function reverseLutFromBitmap( bitmap, lut ) { var k = 0; @@ -747,7 +756,7 @@ EXRLoader.prototype = Object.assign( Object.create( DataTextureLoader.prototype var count = - l; size -= count + 1; - + for ( var i = 0; i < count; i ++ ) { out.push( reader.getUint8( p ++ ) ); @@ -759,7 +768,7 @@ EXRLoader.prototype = Object.assign( Object.create( DataTextureLoader.prototype var count = l; size -= 2; - + var value = reader.getUint8( p ++ ); for ( var i = 0; i < count + 1; i ++ ) { @@ -768,7 +777,6 @@ EXRLoader.prototype = Object.assign( Object.create( DataTextureLoader.prototype } - } } @@ -777,6 +785,400 @@ EXRLoader.prototype = Object.assign( Object.create( DataTextureLoader.prototype } + function lossyDctDecode( cscSet, rowPtrs, channelData, acBuffer, dcBuffer, outBuffer ) { + + var dataView = new DataView( outBuffer.buffer ); + + var width = channelData[ cscSet.idx[ 0 ] ].width; + var height = channelData[ cscSet.idx[ 0 ] ].height; + + var numComp = 3; + + var numFullBlocksX = Math.floor( width / 8.0 ); + var numBlocksX = Math.ceil( width / 8.0 ); + var numBlocksY = Math.ceil( height / 8.0 ); + var leftoverX = width - ( numBlocksX - 1 ) * 8; + var leftoverY = height - ( numBlocksY - 1 ) * 8; + + var currAcComp = { value: 0 }; + var currDcComp = new Array( numComp ); + var dctData = new Array( numComp ); + var halfZigBlock = new Array( numComp ); + var rowBlock = new Array( numComp ); + var rowOffsets = new Array( numComp ); + + for ( let comp = 0; comp < numComp; ++ comp ) { + + rowOffsets[ comp ] = rowPtrs[ cscSet.idx[ comp ] ]; + currDcComp[ comp ] = ( comp < 1 ) ? 0 : currDcComp[ comp - 1 ] + numBlocksX * numBlocksY; + dctData[ comp ] = new Float32Array( 64 ); + halfZigBlock[ comp ] = new Uint16Array( 64 ); + rowBlock[ comp ] = new Uint16Array( numBlocksX * 64 ); + + } + + for ( let blocky = 0; blocky < numBlocksY; ++ blocky ) { + + var maxY = 8; + + if ( blocky == numBlocksY - 1 ) + maxY = leftoverY; + + var maxX = 8; + + for ( let blockx = 0; blockx < numBlocksX; ++ blockx ) { + + if ( blockx == numBlocksX - 1 ) + maxX = leftoverX; + + for ( let comp = 0; comp < numComp; ++ comp ) { + + halfZigBlock[ comp ].fill( 0 ); + + // set block DC component + halfZigBlock[ comp ][ 0 ] = dcBuffer[ currDcComp[ comp ]++ ]; + // set block AC components + unRleAC( currAcComp, acBuffer, halfZigBlock[ comp ] ); + + // UnZigZag block to float + unZigZag( halfZigBlock[ comp ], dctData[ comp ] ); + // decode float dct + dctInverse( dctData[ comp ] ); + + } + + if ( numComp == 3 ) { + + csc709Inverse( dctData ); + + } + + for ( let comp = 0; comp < numComp; ++ comp ) { + + convertToHalf( dctData[ comp ], rowBlock[ comp ], blockx * 64 ); + + } + + } // blockx + + let offset = 0; + + for ( let comp = 0; comp < numComp; ++ comp ) { + + let type = channelData[ cscSet.idx[ comp ] ].type; + + for ( let y = 8 * blocky; y < 8 * blocky + maxY; ++ y ) { + + offset = rowOffsets[ comp ][ y ]; + + for ( let blockx = 0; blockx < numFullBlocksX; ++ blockx ) { + + let src = blockx * 64 + ( ( y & 0x7 ) * 8 ); + + dataView.setUint16( offset + 0 * INT16_SIZE * type, rowBlock[ comp ][ src + 0 ], true ); + dataView.setUint16( offset + 1 * INT16_SIZE * type, rowBlock[ comp ][ src + 1 ], true ); + dataView.setUint16( offset + 2 * INT16_SIZE * type, rowBlock[ comp ][ src + 2 ], true ); + dataView.setUint16( offset + 3 * INT16_SIZE * type, rowBlock[ comp ][ src + 3 ], true ); + + dataView.setUint16( offset + 4 * INT16_SIZE * type, rowBlock[ comp ][ src + 4 ], true ); + dataView.setUint16( offset + 5 * INT16_SIZE * type, rowBlock[ comp ][ src + 5 ], true ); + dataView.setUint16( offset + 6 * INT16_SIZE * type, rowBlock[ comp ][ src + 6 ], true ); + dataView.setUint16( offset + 7 * INT16_SIZE * type, rowBlock[ comp ][ src + 7 ], true ); + + offset += 8 * INT16_SIZE * type; + + } + + } + + // handle partial X blocks + if ( numFullBlocksX != numBlocksX ) { + + for ( let y = 8 * blocky; y < 8 * blocky + maxY; ++ y ) { + + let offset = rowOffsets[ comp ][ y ] + 8 * numFullBlocksX * INT16_SIZE * type; + let src = numFullBlocksX * 64 + ( ( y & 0x7 ) * 8 ); + + for ( let x = 0; x < maxX; ++ x ) { + + dataView.setUint16( offset + x * INT16_SIZE * type, rowBlock[ comp ][ src + x ], true ); + + } + + } + + } + + } // comp + + } // blocky + + var halfRow = new Uint16Array( width ); + var dataView = new DataView( outBuffer.buffer ); + + // convert channels back to float, if needed + for ( var comp = 0; comp < numComp; ++ comp ) { + + channelData[ cscSet.idx[ comp ] ].decoded = true; + var type = channelData[ cscSet.idx[ comp ] ].type; + + if ( channelData[ comp ].type != 2 ) continue; + + for ( var y = 0; y < height; ++ y ) { + + let offset = rowOffsets[ comp ][ y ]; + + for ( var x = 0; x < width; ++ x ) { + + halfRow[ x ] = dataView.getUint16( offset + x * INT16_SIZE * type, true ); + + } + + for ( var x = 0; x < width; ++ x ) { + + dataView.setFloat32( offset + x * INT16_SIZE * type, decodeFloat16( halfRow[ x ] ), true ); + + } + + } + + } + + } + + function unRleAC( currAcComp, acBuffer, halfZigBlock ) { + + var acValue; + var dctComp = 1; + + while ( dctComp < 64 ) { + + acValue = acBuffer[ currAcComp.value ]; + + if ( acValue == 0xff00 ) { + + dctComp = 64; + + } else if ( acValue >> 8 == 0xff ) { + + dctComp += acValue & 0xff; + + } else { + + halfZigBlock[ dctComp ] = acValue; + dctComp++; + + } + + currAcComp.value++; + + } + + } + + function unZigZag( src, dst ) { + + dst[0] = decodeFloat16( src[0] ); + dst[1] = decodeFloat16( src[1] ); + dst[2] = decodeFloat16( src[5] ); + dst[3] = decodeFloat16( src[6] ); + dst[4] = decodeFloat16( src[14] ); + dst[5] = decodeFloat16( src[15] ); + dst[6] = decodeFloat16( src[27] ); + dst[7] = decodeFloat16( src[28] ); + dst[8] = decodeFloat16( src[2] ); + dst[9] = decodeFloat16( src[4] ); + + dst[10] = decodeFloat16( src[7] ); + dst[11] = decodeFloat16( src[13] ); + dst[12] = decodeFloat16( src[16] ); + dst[13] = decodeFloat16( src[26] ); + dst[14] = decodeFloat16( src[29] ); + dst[15] = decodeFloat16( src[42] ); + dst[16] = decodeFloat16( src[3] ); + dst[17] = decodeFloat16( src[8] ); + dst[18] = decodeFloat16( src[12] ); + dst[19] = decodeFloat16( src[17] ); + + dst[20] = decodeFloat16( src[25] ); + dst[21] = decodeFloat16( src[30] ); + dst[22] = decodeFloat16( src[41] ); + dst[23] = decodeFloat16( src[43] ); + dst[24] = decodeFloat16( src[9] ); + dst[25] = decodeFloat16( src[11] ); + dst[26] = decodeFloat16( src[18] ); + dst[27] = decodeFloat16( src[24] ); + dst[28] = decodeFloat16( src[31] ); + dst[29] = decodeFloat16( src[40] ); + + dst[30] = decodeFloat16( src[44] ); + dst[31] = decodeFloat16( src[53] ); + dst[32] = decodeFloat16( src[10] ); + dst[33] = decodeFloat16( src[19] ); + dst[34] = decodeFloat16( src[23] ); + dst[35] = decodeFloat16( src[32] ); + dst[36] = decodeFloat16( src[39] ); + dst[37] = decodeFloat16( src[45] ); + dst[38] = decodeFloat16( src[52] ); + dst[39] = decodeFloat16( src[54] ); + + dst[40] = decodeFloat16( src[20] ); + dst[41] = decodeFloat16( src[22] ); + dst[42] = decodeFloat16( src[33] ); + dst[43] = decodeFloat16( src[38] ); + dst[44] = decodeFloat16( src[46] ); + dst[45] = decodeFloat16( src[51] ); + dst[46] = decodeFloat16( src[55] ); + dst[47] = decodeFloat16( src[60] ); + dst[48] = decodeFloat16( src[21] ); + dst[49] = decodeFloat16( src[34] ); + + dst[50] = decodeFloat16( src[37] ); + dst[51] = decodeFloat16( src[47] ); + dst[52] = decodeFloat16( src[50] ); + dst[53] = decodeFloat16( src[56] ); + dst[54] = decodeFloat16( src[59] ); + dst[55] = decodeFloat16( src[61] ); + dst[56] = decodeFloat16( src[35] ); + dst[57] = decodeFloat16( src[36] ); + dst[58] = decodeFloat16( src[48] ); + dst[59] = decodeFloat16( src[49] ); + + dst[60] = decodeFloat16( src[57] ); + dst[61] = decodeFloat16( src[58] ); + dst[62] = decodeFloat16( src[62] ); + dst[63] = decodeFloat16( src[63] ); + + } + + function dctInverse( data ) { + + const a = .5 * Math.cos ( 3.14159 / 4.0 ); + const b = .5 * Math.cos ( 3.14159 / 16.0 ); + const c = .5 * Math.cos ( 3.14159 / 8.0 ); + const d = .5 * Math.cos ( 3.*3.14159 / 16.0 ); + const e = .5 * Math.cos ( 5.*3.14159 / 16.0 ); + const f = .5 * Math.cos ( 3.*3.14159 / 8.0 ); + const g = .5 * Math.cos ( 7.*3.14159 / 16.0 ); + + var alpha = new Array( 4 ); + var beta = new Array( 4 ); + var theta = new Array( 4 ); + var gamma = new Array( 4 ); + + for ( var row = 0; row < 8; ++ row ) { + + var rowPtr = row * 8; + + alpha[ 0 ] = c * data[ rowPtr + 2 ]; + alpha[ 1 ] = f * data[ rowPtr + 2 ]; + alpha[ 2 ] = c * data[ rowPtr + 6 ]; + alpha[ 3 ] = f * data[ rowPtr + 6 ]; + + beta[ 0 ] = b * data[ rowPtr + 1 ] + d * data[ rowPtr + 3 ] + e * data[ rowPtr + 5 ] + g * data[ rowPtr + 7 ]; + beta[ 1 ] = d * data[ rowPtr + 1 ] - g * data[ rowPtr + 3 ] - b * data[ rowPtr + 5 ] - e * data[ rowPtr + 7 ]; + beta[ 2 ] = e * data[ rowPtr + 1 ] - b * data[ rowPtr + 3 ] + g * data[ rowPtr + 5 ] + d * data[ rowPtr + 7 ]; + beta[ 3 ] = g * data[ rowPtr + 1 ] - e * data[ rowPtr + 3 ] + d * data[ rowPtr + 5 ] - b * data[ rowPtr + 7 ]; + + theta[ 0 ] = a * ( data[ rowPtr + 0 ] + data[ rowPtr + 4 ] ); + theta[ 3 ] = a * ( data[ rowPtr + 0 ] - data[ rowPtr + 4 ] ); + theta[ 1 ] = alpha[ 0 ] + alpha[ 3 ]; + theta[ 2 ] = alpha[ 1 ] - alpha[ 2 ]; + + gamma[ 0 ] = theta[ 0 ] + theta[ 1 ]; + gamma[ 1 ] = theta[ 3 ] + theta[ 2 ]; + gamma[ 2 ] = theta[ 3 ] - theta[ 2 ]; + gamma[ 3 ] = theta[ 0 ] - theta[ 1 ]; + + data[ rowPtr + 0 ] = gamma[ 0 ] + beta[ 0 ]; + data[ rowPtr + 1 ] = gamma[ 1 ] + beta[ 1 ]; + data[ rowPtr + 2 ] = gamma[ 2 ] + beta[ 2 ]; + data[ rowPtr + 3 ] = gamma[ 3 ] + beta[ 3 ]; + + data[ rowPtr + 4 ] = gamma[ 3 ] - beta[ 3 ]; + data[ rowPtr + 5 ] = gamma[ 2 ] - beta[ 2 ]; + data[ rowPtr + 6 ] = gamma[ 1 ] - beta[ 1 ]; + data[ rowPtr + 7 ] = gamma[ 0 ] - beta[ 0 ]; + + } + + for ( var column = 0; column < 8; ++ column ) { + + alpha[ 0 ] = c * data[ 16 + column ]; + alpha[ 1 ] = f * data[ 16 + column ]; + alpha[ 2 ] = c * data[ 48 + column ]; + alpha[ 3 ] = f * data[ 48 + column ]; + + beta[ 0 ] = b * data[ 8 + column ] + d * data[ 24 + column ] + e * data[ 40 + column ] + g * data[ 56 + column ]; + beta[ 1 ] = d * data[ 8 + column ] - g * data[ 24 + column ] - b * data[ 40 + column ] - e * data[ 56 + column ]; + beta[ 2 ] = e * data[ 8 + column ] - b * data[ 24 + column ] + g * data[ 40 + column ] + d * data[ 56 + column ]; + beta[ 3 ] = g * data [8 + column ] - e * data[ 24 + column ] + d * data[ 40 + column ] - b * data[ 56 + column ]; + + theta[ 0 ] = a * ( data[ column ] + data[ 32 + column ] ); + theta[ 3 ] = a * ( data[ column ] - data[ 32 + column ] ); + + theta[ 1 ] = alpha[ 0 ] + alpha[ 3 ]; + theta[ 2 ] = alpha[ 1 ] - alpha[ 2 ]; + + gamma[ 0 ] = theta[ 0 ] + theta[ 1 ]; + gamma[ 1 ] = theta[ 3 ] + theta[ 2 ]; + gamma[ 2 ] = theta[ 3 ] - theta[ 2 ]; + gamma[ 3 ] = theta[ 0 ] - theta[ 1 ]; + + data[ column ] = gamma[ 0 ] + beta[ 0 ]; + data[ 8 + column ] = gamma[ 1 ] + beta[ 1 ]; + data[ 16 + column ] = gamma[ 2 ] + beta[ 2 ]; + data[ 24 + column ] = gamma[ 3 ] + beta[ 3 ]; + + data[ 32 + column ] = gamma[ 3 ] - beta[ 3 ]; + data[ 40 + column ] = gamma[ 2 ] - beta[ 2 ]; + data[ 48 + column ] = gamma[ 1 ] - beta[ 1 ]; + data[ 56 + column ] = gamma[ 0 ] - beta[ 0 ]; + + } + + } + + function csc709Inverse( data ) { + + for ( var i = 0; i < 64; ++ i ) { + + var y = data[ 0 ][ i ]; + var cb = data[ 1 ][ i ]; + var cr = data[ 2 ][ i ]; + + data[ 0 ][ i ] = y + 1.5747 * cr; + data[ 1 ][ i ] = y - 0.1873 * cb - 0.4682 * cr; + data[ 2 ][ i ] = y + 1.8556 * cb; + + } + + } + + function convertToHalf( src, dst, idx ) { + + for ( var i = 0; i < 64; ++ i ) { + + dst[ idx + i ] = encodeFloat16( toLinear( src[ i ] ) ); + + } + + } + + function toLinear( float ) { + + if ( float <= 1 ) { + + return Math.sign( float ) * Math.pow( Math.abs( float ), 2.2 ); + + } else { + + return Math.sign( float ) * Math.pow( logBase, Math.abs( float ) - 1.0 ); + + } + + } + function uncompressRAW( info ) { return new DataView( info.array.buffer, info.offset.value, info.size ); @@ -922,6 +1324,223 @@ EXRLoader.prototype = Object.assign( Object.create( DataTextureLoader.prototype } + function uncompressDWA( info ) { + + var inDataView = info.viewer; + var inOffset = { value: info.offset.value }; + var outBuffer = new Uint8Array( info.width * info.lines * ( EXRHeader.channels.length * info.type * INT16_SIZE ) ); + + // Read compression header information + var dwaHeader = { + + version: parseInt64( inDataView, inOffset ), + unknownUncompressedSize: parseInt64( inDataView, inOffset ), + unknownCompressedSize: parseInt64( inDataView, inOffset ), + acCompressedSize: parseInt64( inDataView, inOffset ), + dcCompressedSize: parseInt64( inDataView, inOffset ), + rleCompressedSize: parseInt64( inDataView, inOffset ), + rleUncompressedSize: parseInt64( inDataView, inOffset ), + rleRawSize: parseInt64( inDataView, inOffset ), + totalAcUncompressedCount: parseInt64( inDataView, inOffset ), + totalDcUncompressedCount: parseInt64( inDataView, inOffset ), + acCompression: parseInt64( inDataView, inOffset ) + + } + + if ( dwaHeader.version < 2 ) + throw 'EXRLoader.parse: ' + EXRHeader.compression + ' version ' + dwaHeader.version + ' is unsupported'; + + // Read channel ruleset information + var channelRules = new Array(); + var ruleSize = parseUint16( inDataView, inOffset ) - INT16_SIZE; + + while ( ruleSize > 0 ) { + + var name = parseNullTerminatedString( inDataView.buffer, inOffset ); + var value = parseUint8( inDataView, inOffset ); + var compression = ( value >> 2 ) & 3; + var csc = ( value >> 4 ) - 1; + var index = new Int8Array( [ csc ] )[ 0 ]; + var type = parseUint8( inDataView, inOffset ); + + channelRules.push( { + name: name, + index: index, + type: type, + compression: compression, + } ); + + ruleSize -= name.length + 3; + + } + + // Classify channels + var channels = EXRHeader.channels; + var channelData = new Array( info.channels ); + + for ( var i = 0; i < info.channels; ++ i ) { + + var cd = channelData[ i ] = {}; + var channel = channels[ i ]; + + cd.name = channel.name; + cd.compression = UNKNOWN; + cd.decoded = false; + cd.type = channel.pixelType; + cd.pLinear = channel.pLinear; + cd.width = info.width; + cd.height = info.lines; + + } + + var cscSet = { + idx: new Array( 3 ) + }; + + for ( var offset = 0; offset < info.channels; ++ offset ) { + + var cd = channelData[ offset ]; + + for ( var i = 0; i < channelRules.length; ++ i ) { + + var rule = channelRules[ i ]; + + if ( cd.name == rule.name ) { + + cd.compression = rule.compression; + + if ( rule.index >= 0 ) { + + cscSet.idx[ rule.index ] = offset; + + } + + cd.offset = offset; + + } + + } + + } + + // Read DCT - AC component data + if ( dwaHeader.acCompressedSize > 0 ) { + + switch ( dwaHeader.acCompression ) { + + case STATIC_HUFFMAN: + + var acBuffer = new Uint16Array( dwaHeader.totalAcUncompressedCount ); + hufUncompress( info.array, inDataView, inOffset, dwaHeader.acCompressedSize, acBuffer, dwaHeader.totalAcUncompressedCount ); + break; + + case DEFLATE: + + var compressed = info.array.slice( inOffset.value, inOffset.value + dwaHeader.totalAcUncompressedCount ); + var inflate = new Zlib.Inflate( compressed, { resize: true, verify: true } ); + var acBuffer = new Uint16Array( inflate.decompress().buffer ); + inOffset.value += dwaHeader.totalAcUncompressedCount; + break; + + } + + + } + + // Read DCT - DC component data + if ( dwaHeader.dcCompressedSize > 0 ) { + + var zlibInfo = { + array: info.array, + offset: inOffset, + size: dwaHeader.dcCompressedSize + } + var dcBuffer = new Uint16Array( uncompressZIP( zlibInfo ).buffer ); + inOffset.value += dwaHeader.dcCompressedSize; + + } + + // Read RLE compressed data + if ( dwaHeader.rleRawSize > 0 ) { + + var compressed = info.array.slice( inOffset.value, inOffset.value + dwaHeader.rleCompressedSize ); + var inflate = new Zlib.Inflate( compressed, { resize: true, verify: true } ); + var rleBuffer = decodeRunLength( inflate.decompress().buffer ); + + inOffset.value += dwaHeader.rleCompressedSize; + + } + + // Prepare outbuffer data offset + var outBufferEnd = 0; + var rowOffsets = new Array( channelData.length ); + for ( var i = 0; i < rowOffsets.length; ++ i ) { + + rowOffsets[ i ] = new Array(); + + } + + for ( var y = 0; y < info.lines; ++ y ) { + + for ( var chan = 0; chan < channelData.length; ++ chan ) { + + rowOffsets[ chan ].push( outBufferEnd ); + outBufferEnd += channelData[ chan ].width * info.type * INT16_SIZE; + + } + + } + + // Lossy DCT decode RGB channels + lossyDctDecode( cscSet, rowOffsets, channelData, acBuffer, dcBuffer, outBuffer ); + + // Decode other channels + for ( var i = 0; i < channelData.length; ++ i ) { + + var cd = channelData[ i ]; + + if ( cd.decoded ) continue; + + switch ( cd.compression ) { + + case RLE: + + var row = 0; + var rleOffset = 0; + + for ( var y = 0; y < info.lines; ++ y ) { + + var rowOffsetBytes = rowOffsets[ i ][ row ]; + + for ( var x = 0; x < cd.width; ++ x ) { + + for ( var byte = 0; byte < INT16_SIZE * cd.type; ++ byte ) { + + outBuffer[ rowOffsetBytes++ ] = rleBuffer[ rleOffset + byte * cd.width * cd.height ]; + + } + + rleOffset++ + + } + + row++; + + } + + break; + + default: + throw 'EXRLoader.parse: unsupported channel compression'; + + } + + } + + return new DataView( outBuffer.buffer ); + + } + function parseNullTerminatedString( buffer, offset ) { var uintBuffer = new Uint8Array( buffer ); @@ -995,6 +1614,16 @@ EXRLoader.prototype = Object.assign( Object.create( DataTextureLoader.prototype } + function parseInt64( dataView, offset ) { + + var int = Number( dataView.getBigInt64( offset.value, true ) ); + + offset.value += ULONG_SIZE; + + return int; + + } + function parseFloat32( dataView, offset ) { var float = dataView.getFloat32( offset.value, true ); @@ -1023,6 +1652,46 @@ EXRLoader.prototype = Object.assign( Object.create( DataTextureLoader.prototype } + // https://stackoverflow.com/questions/32633585/how-do-you-convert-to-half-floats-in-javascript + var encodeFloat16 = ( function() { + + var floatView = new Float32Array(1); + var int32View = new Int32Array(floatView.buffer); + + return function toHalf( fval ) { + + floatView[ 0 ] = fval; + var fbits = int32View[ 0 ]; + var sign = ( fbits >> 16 ) & 0x8000; // sign only + var val = ( fbits & 0x7fffffff ) + 0x1000; // rounded value + + if ( val >= 0x47800000 ) { // might be or become NaN/Inf + if ( ( fbits & 0x7fffffff ) >= 0x47800000 ) { + // is or must become NaN/Inf + if( val < 0x7f800000 ) { // was value but too large + return sign | 0x7c00; // make it +/-Inf + } + return sign | 0x7c00 | // remains +/-Inf or NaN + ( fbits & 0x007fffff ) >> 13; // keep NaN (and Inf) bits + } + return sign | 0x7bff; // unrounded not quite Inf + } + if ( val >= 0x38800000 ) { // remains normalized value + return sign | val - 0x38000000 >> 13; // exp - 127 + 15 + } + if ( val < 0x33000000 ) { // too small for subnormal + return sign; // becomes +/-0 + } + val = ( fbits & 0x7fffffff ) >> 23; // tmp exp for subnormal calc + return sign | ( ( fbits & 0x7fffff | 0x800000 ) // add subnormal bit + + ( 0x800000 >>> val - 102 ) // round depending on cut off + >> 126 - val ); // div by 2^(1-(exp-127+15)) and >> 13 | exp= + + }; + + } () ); + + function parseUint16( dataView, offset ) { var Uint16 = dataView.getUint16( offset.value, true ); @@ -1256,6 +1925,18 @@ EXRLoader.prototype = Object.assign( Object.create( DataTextureLoader.prototype uncompress = uncompressPIZ; break; + case 'DWAA_COMPRESSION': + + scanlineBlockSize = 32; + uncompress = uncompressDWA; + break; + + case 'DWAB_COMPRESSION': + + scanlineBlockSize = 256; + uncompress = uncompressDWA; + break; + default: throw 'EXRLoader.parse: ' + EXRHeader.compression + ' is unsupported'; From ca79fec56d0232396cd4e2511d06816b0436577c Mon Sep 17 00:00:00 2001 From: Guilherme Avila Date: Mon, 13 Jan 2020 19:53:23 -0300 Subject: [PATCH 2/2] EXRLoader: formatting --- examples/js/loaders/EXRLoader.js | 345 ++++++++++++++++-------------- examples/jsm/loaders/EXRLoader.js | 345 ++++++++++++++++-------------- 2 files changed, 362 insertions(+), 328 deletions(-) diff --git a/examples/js/loaders/EXRLoader.js b/examples/js/loaders/EXRLoader.js index 8eb9e884d905cd..10adffe87390a0 100644 --- a/examples/js/loaders/EXRLoader.js +++ b/examples/js/loaders/EXRLoader.js @@ -745,7 +745,7 @@ THREE.EXRLoader.prototype = Object.assign( Object.create( THREE.DataTextureLoade var count = - l; size -= count + 1; - + for ( var i = 0; i < count; i ++ ) { out.push( reader.getUint8( p ++ ) ); @@ -757,7 +757,7 @@ THREE.EXRLoader.prototype = Object.assign( Object.create( THREE.DataTextureLoade var count = l; size -= 2; - + var value = reader.getUint8( p ++ ); for ( var i = 0; i < count + 1; i ++ ) { @@ -788,7 +788,7 @@ THREE.EXRLoader.prototype = Object.assign( Object.create( THREE.DataTextureLoade var numBlocksY = Math.ceil( height / 8.0 ); var leftoverX = width - ( numBlocksX - 1 ) * 8; var leftoverY = height - ( numBlocksY - 1 ) * 8; - + var currAcComp = { value: 0 }; var currDcComp = new Array( numComp ); var dctData = new Array( numComp ); @@ -825,7 +825,7 @@ THREE.EXRLoader.prototype = Object.assign( Object.create( THREE.DataTextureLoade halfZigBlock[ comp ].fill( 0 ); // set block DC component - halfZigBlock[ comp ][ 0 ] = dcBuffer[ currDcComp[ comp ]++ ]; + halfZigBlock[ comp ][ 0 ] = dcBuffer[ currDcComp[ comp ] ++ ]; // set block AC components unRleAC( currAcComp, acBuffer, halfZigBlock[ comp ] ); @@ -857,13 +857,13 @@ THREE.EXRLoader.prototype = Object.assign( Object.create( THREE.DataTextureLoade let type = channelData[ cscSet.idx[ comp ] ].type; for ( let y = 8 * blocky; y < 8 * blocky + maxY; ++ y ) { - + offset = rowOffsets[ comp ][ y ]; - + for ( let blockx = 0; blockx < numFullBlocksX; ++ blockx ) { - + let src = blockx * 64 + ( ( y & 0x7 ) * 8 ); - + dataView.setUint16( offset + 0 * INT16_SIZE * type, rowBlock[ comp ][ src + 0 ], true ); dataView.setUint16( offset + 1 * INT16_SIZE * type, rowBlock[ comp ][ src + 1 ], true ); dataView.setUint16( offset + 2 * INT16_SIZE * type, rowBlock[ comp ][ src + 2 ], true ); @@ -875,29 +875,29 @@ THREE.EXRLoader.prototype = Object.assign( Object.create( THREE.DataTextureLoade dataView.setUint16( offset + 7 * INT16_SIZE * type, rowBlock[ comp ][ src + 7 ], true ); offset += 8 * INT16_SIZE * type; - + } - + } // handle partial X blocks if ( numFullBlocksX != numBlocksX ) { for ( let y = 8 * blocky; y < 8 * blocky + maxY; ++ y ) { - + let offset = rowOffsets[ comp ][ y ] + 8 * numFullBlocksX * INT16_SIZE * type; let src = numFullBlocksX * 64 + ( ( y & 0x7 ) * 8 ); - + for ( let x = 0; x < maxX; ++ x ) { dataView.setUint16( offset + x * INT16_SIZE * type, rowBlock[ comp ][ src + x ], true ); } - + } } - + } // comp } // blocky @@ -955,11 +955,11 @@ THREE.EXRLoader.prototype = Object.assign( Object.create( THREE.DataTextureLoade } else { halfZigBlock[ dctComp ] = acValue; - dctComp++; + dctComp ++; } - currAcComp.value++; + currAcComp.value ++; } @@ -967,88 +967,88 @@ THREE.EXRLoader.prototype = Object.assign( Object.create( THREE.DataTextureLoade function unZigZag( src, dst ) { - dst[0] = decodeFloat16( src[0] ); - dst[1] = decodeFloat16( src[1] ); - dst[2] = decodeFloat16( src[5] ); - dst[3] = decodeFloat16( src[6] ); - dst[4] = decodeFloat16( src[14] ); - dst[5] = decodeFloat16( src[15] ); - dst[6] = decodeFloat16( src[27] ); - dst[7] = decodeFloat16( src[28] ); - dst[8] = decodeFloat16( src[2] ); - dst[9] = decodeFloat16( src[4] ); - - dst[10] = decodeFloat16( src[7] ); - dst[11] = decodeFloat16( src[13] ); - dst[12] = decodeFloat16( src[16] ); - dst[13] = decodeFloat16( src[26] ); - dst[14] = decodeFloat16( src[29] ); - dst[15] = decodeFloat16( src[42] ); - dst[16] = decodeFloat16( src[3] ); - dst[17] = decodeFloat16( src[8] ); - dst[18] = decodeFloat16( src[12] ); - dst[19] = decodeFloat16( src[17] ); - - dst[20] = decodeFloat16( src[25] ); - dst[21] = decodeFloat16( src[30] ); - dst[22] = decodeFloat16( src[41] ); - dst[23] = decodeFloat16( src[43] ); - dst[24] = decodeFloat16( src[9] ); - dst[25] = decodeFloat16( src[11] ); - dst[26] = decodeFloat16( src[18] ); - dst[27] = decodeFloat16( src[24] ); - dst[28] = decodeFloat16( src[31] ); - dst[29] = decodeFloat16( src[40] ); - - dst[30] = decodeFloat16( src[44] ); - dst[31] = decodeFloat16( src[53] ); - dst[32] = decodeFloat16( src[10] ); - dst[33] = decodeFloat16( src[19] ); - dst[34] = decodeFloat16( src[23] ); - dst[35] = decodeFloat16( src[32] ); - dst[36] = decodeFloat16( src[39] ); - dst[37] = decodeFloat16( src[45] ); - dst[38] = decodeFloat16( src[52] ); - dst[39] = decodeFloat16( src[54] ); - - dst[40] = decodeFloat16( src[20] ); - dst[41] = decodeFloat16( src[22] ); - dst[42] = decodeFloat16( src[33] ); - dst[43] = decodeFloat16( src[38] ); - dst[44] = decodeFloat16( src[46] ); - dst[45] = decodeFloat16( src[51] ); - dst[46] = decodeFloat16( src[55] ); - dst[47] = decodeFloat16( src[60] ); - dst[48] = decodeFloat16( src[21] ); - dst[49] = decodeFloat16( src[34] ); - - dst[50] = decodeFloat16( src[37] ); - dst[51] = decodeFloat16( src[47] ); - dst[52] = decodeFloat16( src[50] ); - dst[53] = decodeFloat16( src[56] ); - dst[54] = decodeFloat16( src[59] ); - dst[55] = decodeFloat16( src[61] ); - dst[56] = decodeFloat16( src[35] ); - dst[57] = decodeFloat16( src[36] ); - dst[58] = decodeFloat16( src[48] ); - dst[59] = decodeFloat16( src[49] ); - - dst[60] = decodeFloat16( src[57] ); - dst[61] = decodeFloat16( src[58] ); - dst[62] = decodeFloat16( src[62] ); - dst[63] = decodeFloat16( src[63] ); + dst[ 0 ] = decodeFloat16( src[ 0 ] ); + dst[ 1 ] = decodeFloat16( src[ 1 ] ); + dst[ 2 ] = decodeFloat16( src[ 5 ] ); + dst[ 3 ] = decodeFloat16( src[ 6 ] ); + dst[ 4 ] = decodeFloat16( src[ 14 ] ); + dst[ 5 ] = decodeFloat16( src[ 15 ] ); + dst[ 6 ] = decodeFloat16( src[ 27 ] ); + dst[ 7 ] = decodeFloat16( src[ 28 ] ); + dst[ 8 ] = decodeFloat16( src[ 2 ] ); + dst[ 9 ] = decodeFloat16( src[ 4 ] ); + + dst[ 10 ] = decodeFloat16( src[ 7 ] ); + dst[ 11 ] = decodeFloat16( src[ 13 ] ); + dst[ 12 ] = decodeFloat16( src[ 16 ] ); + dst[ 13 ] = decodeFloat16( src[ 26 ] ); + dst[ 14 ] = decodeFloat16( src[ 29 ] ); + dst[ 15 ] = decodeFloat16( src[ 42 ] ); + dst[ 16 ] = decodeFloat16( src[ 3 ] ); + dst[ 17 ] = decodeFloat16( src[ 8 ] ); + dst[ 18 ] = decodeFloat16( src[ 12 ] ); + dst[ 19 ] = decodeFloat16( src[ 17 ] ); + + dst[ 20 ] = decodeFloat16( src[ 25 ] ); + dst[ 21 ] = decodeFloat16( src[ 30 ] ); + dst[ 22 ] = decodeFloat16( src[ 41 ] ); + dst[ 23 ] = decodeFloat16( src[ 43 ] ); + dst[ 24 ] = decodeFloat16( src[ 9 ] ); + dst[ 25 ] = decodeFloat16( src[ 11 ] ); + dst[ 26 ] = decodeFloat16( src[ 18 ] ); + dst[ 27 ] = decodeFloat16( src[ 24 ] ); + dst[ 28 ] = decodeFloat16( src[ 31 ] ); + dst[ 29 ] = decodeFloat16( src[ 40 ] ); + + dst[ 30 ] = decodeFloat16( src[ 44 ] ); + dst[ 31 ] = decodeFloat16( src[ 53 ] ); + dst[ 32 ] = decodeFloat16( src[ 10 ] ); + dst[ 33 ] = decodeFloat16( src[ 19 ] ); + dst[ 34 ] = decodeFloat16( src[ 23 ] ); + dst[ 35 ] = decodeFloat16( src[ 32 ] ); + dst[ 36 ] = decodeFloat16( src[ 39 ] ); + dst[ 37 ] = decodeFloat16( src[ 45 ] ); + dst[ 38 ] = decodeFloat16( src[ 52 ] ); + dst[ 39 ] = decodeFloat16( src[ 54 ] ); + + dst[ 40 ] = decodeFloat16( src[ 20 ] ); + dst[ 41 ] = decodeFloat16( src[ 22 ] ); + dst[ 42 ] = decodeFloat16( src[ 33 ] ); + dst[ 43 ] = decodeFloat16( src[ 38 ] ); + dst[ 44 ] = decodeFloat16( src[ 46 ] ); + dst[ 45 ] = decodeFloat16( src[ 51 ] ); + dst[ 46 ] = decodeFloat16( src[ 55 ] ); + dst[ 47 ] = decodeFloat16( src[ 60 ] ); + dst[ 48 ] = decodeFloat16( src[ 21 ] ); + dst[ 49 ] = decodeFloat16( src[ 34 ] ); + + dst[ 50 ] = decodeFloat16( src[ 37 ] ); + dst[ 51 ] = decodeFloat16( src[ 47 ] ); + dst[ 52 ] = decodeFloat16( src[ 50 ] ); + dst[ 53 ] = decodeFloat16( src[ 56 ] ); + dst[ 54 ] = decodeFloat16( src[ 59 ] ); + dst[ 55 ] = decodeFloat16( src[ 61 ] ); + dst[ 56 ] = decodeFloat16( src[ 35 ] ); + dst[ 57 ] = decodeFloat16( src[ 36 ] ); + dst[ 58 ] = decodeFloat16( src[ 48 ] ); + dst[ 59 ] = decodeFloat16( src[ 49 ] ); + + dst[ 60 ] = decodeFloat16( src[ 57 ] ); + dst[ 61 ] = decodeFloat16( src[ 58 ] ); + dst[ 62 ] = decodeFloat16( src[ 62 ] ); + dst[ 63 ] = decodeFloat16( src[ 63 ] ); } function dctInverse( data ) { - const a = .5 * Math.cos ( 3.14159 / 4.0 ); - const b = .5 * Math.cos ( 3.14159 / 16.0 ); - const c = .5 * Math.cos ( 3.14159 / 8.0 ); - const d = .5 * Math.cos ( 3.*3.14159 / 16.0 ); - const e = .5 * Math.cos ( 5.*3.14159 / 16.0 ); - const f = .5 * Math.cos ( 3.*3.14159 / 8.0 ); - const g = .5 * Math.cos ( 7.*3.14159 / 16.0 ); + const a = 0.5 * Math.cos( 3.14159 / 4.0 ); + const b = 0.5 * Math.cos( 3.14159 / 16.0 ); + const c = 0.5 * Math.cos( 3.14159 / 8.0 ); + const d = 0.5 * Math.cos( 3.0 * 3.14159 / 16.0 ); + const e = 0.5 * Math.cos( 5.0 * 3.14159 / 16.0 ); + const f = 0.5 * Math.cos( 3.0 * 3.14159 / 8.0 ); + const g = 0.5 * Math.cos( 7.0 * 3.14159 / 16.0 ); var alpha = new Array( 4 ); var beta = new Array( 4 ); @@ -1059,31 +1059,31 @@ THREE.EXRLoader.prototype = Object.assign( Object.create( THREE.DataTextureLoade var rowPtr = row * 8; - alpha[ 0 ] = c * data[ rowPtr + 2 ]; - alpha[ 1 ] = f * data[ rowPtr + 2 ]; - alpha[ 2 ] = c * data[ rowPtr + 6 ]; - alpha[ 3 ] = f * data[ rowPtr + 6 ]; - + alpha[ 0 ] = c * data[ rowPtr + 2 ]; + alpha[ 1 ] = f * data[ rowPtr + 2 ]; + alpha[ 2 ] = c * data[ rowPtr + 6 ]; + alpha[ 3 ] = f * data[ rowPtr + 6 ]; + beta[ 0 ] = b * data[ rowPtr + 1 ] + d * data[ rowPtr + 3 ] + e * data[ rowPtr + 5 ] + g * data[ rowPtr + 7 ]; beta[ 1 ] = d * data[ rowPtr + 1 ] - g * data[ rowPtr + 3 ] - b * data[ rowPtr + 5 ] - e * data[ rowPtr + 7 ]; beta[ 2 ] = e * data[ rowPtr + 1 ] - b * data[ rowPtr + 3 ] + g * data[ rowPtr + 5 ] + d * data[ rowPtr + 7 ]; beta[ 3 ] = g * data[ rowPtr + 1 ] - e * data[ rowPtr + 3 ] + d * data[ rowPtr + 5 ] - b * data[ rowPtr + 7 ]; - + theta[ 0 ] = a * ( data[ rowPtr + 0 ] + data[ rowPtr + 4 ] ); theta[ 3 ] = a * ( data[ rowPtr + 0 ] - data[ rowPtr + 4 ] ); - theta[ 1 ] = alpha[ 0 ] + alpha[ 3 ]; - theta[ 2 ] = alpha[ 1 ] - alpha[ 2 ]; - + theta[ 1 ] = alpha[ 0 ] + alpha[ 3 ]; + theta[ 2 ] = alpha[ 1 ] - alpha[ 2 ]; + gamma[ 0 ] = theta[ 0 ] + theta[ 1 ]; gamma[ 1 ] = theta[ 3 ] + theta[ 2 ]; gamma[ 2 ] = theta[ 3 ] - theta[ 2 ]; gamma[ 3 ] = theta[ 0 ] - theta[ 1 ]; - + data[ rowPtr + 0 ] = gamma[ 0 ] + beta[ 0 ]; data[ rowPtr + 1 ] = gamma[ 1 ] + beta[ 1 ]; data[ rowPtr + 2 ] = gamma[ 2 ] + beta[ 2 ]; data[ rowPtr + 3 ] = gamma[ 3 ] + beta[ 3 ]; - + data[ rowPtr + 4 ] = gamma[ 3 ] - beta[ 3 ]; data[ rowPtr + 5 ] = gamma[ 2 ] - beta[ 2 ]; data[ rowPtr + 6 ] = gamma[ 1 ] - beta[ 1 ]; @@ -1093,29 +1093,29 @@ THREE.EXRLoader.prototype = Object.assign( Object.create( THREE.DataTextureLoade for ( var column = 0; column < 8; ++ column ) { - alpha[ 0 ] = c * data[ 16 + column ]; - alpha[ 1 ] = f * data[ 16 + column ]; - alpha[ 2 ] = c * data[ 48 + column ]; - alpha[ 3 ] = f * data[ 48 + column ]; + alpha[ 0 ] = c * data[ 16 + column ]; + alpha[ 1 ] = f * data[ 16 + column ]; + alpha[ 2 ] = c * data[ 48 + column ]; + alpha[ 3 ] = f * data[ 48 + column ]; - beta[ 0 ] = b * data[ 8 + column ] + d * data[ 24 + column ] + e * data[ 40 + column ] + g * data[ 56 + column ]; - beta[ 1 ] = d * data[ 8 + column ] - g * data[ 24 + column ] - b * data[ 40 + column ] - e * data[ 56 + column ]; - beta[ 2 ] = e * data[ 8 + column ] - b * data[ 24 + column ] + g * data[ 40 + column ] + d * data[ 56 + column ]; - beta[ 3 ] = g * data [8 + column ] - e * data[ 24 + column ] + d * data[ 40 + column ] - b * data[ 56 + column ]; + beta[ 0 ] = b * data[ 8 + column ] + d * data[ 24 + column ] + e * data[ 40 + column ] + g * data[ 56 + column ]; + beta[ 1 ] = d * data[ 8 + column ] - g * data[ 24 + column ] - b * data[ 40 + column ] - e * data[ 56 + column ]; + beta[ 2 ] = e * data[ 8 + column ] - b * data[ 24 + column ] + g * data[ 40 + column ] + d * data[ 56 + column ]; + beta[ 3 ] = g * data[ 8 + column ] - e * data[ 24 + column ] + d * data[ 40 + column ] - b * data[ 56 + column ]; theta[ 0 ] = a * ( data[ column ] + data[ 32 + column ] ); theta[ 3 ] = a * ( data[ column ] - data[ 32 + column ] ); - theta[ 1 ] = alpha[ 0 ] + alpha[ 3 ]; - theta[ 2 ] = alpha[ 1 ] - alpha[ 2 ]; + theta[ 1 ] = alpha[ 0 ] + alpha[ 3 ]; + theta[ 2 ] = alpha[ 1 ] - alpha[ 2 ]; gamma[ 0 ] = theta[ 0 ] + theta[ 1 ]; gamma[ 1 ] = theta[ 3 ] + theta[ 2 ]; gamma[ 2 ] = theta[ 3 ] - theta[ 2 ]; gamma[ 3 ] = theta[ 0 ] - theta[ 1 ]; - data[ column ] = gamma[ 0 ] + beta[ 0 ]; - data[ 8 + column ] = gamma[ 1 ] + beta[ 1 ]; + data[ 0 + column ] = gamma[ 0 ] + beta[ 0 ]; + data[ 8 + column ] = gamma[ 1 ] + beta[ 1 ]; data[ 16 + column ] = gamma[ 2 ] + beta[ 2 ]; data[ 24 + column ] = gamma[ 3 ] + beta[ 3 ]; @@ -1334,15 +1334,15 @@ THREE.EXRLoader.prototype = Object.assign( Object.create( THREE.DataTextureLoade totalDcUncompressedCount: parseInt64( inDataView, inOffset ), acCompression: parseInt64( inDataView, inOffset ) - } + }; - if ( dwaHeader.version < 2 ) + if ( dwaHeader.version < 2 ) throw 'EXRLoader.parse: ' + EXRHeader.compression + ' version ' + dwaHeader.version + ' is unsupported'; // Read channel ruleset information var channelRules = new Array(); var ruleSize = parseUint16( inDataView, inOffset ) - INT16_SIZE; - + while ( ruleSize > 0 ) { var name = parseNullTerminatedString( inDataView.buffer, inOffset ); @@ -1352,10 +1352,10 @@ THREE.EXRLoader.prototype = Object.assign( Object.create( THREE.DataTextureLoade var index = new Int8Array( [ csc ] )[ 0 ]; var type = parseUint8( inDataView, inOffset ); - channelRules.push( { - name: name, - index: index, - type: type, + channelRules.push( { + name: name, + index: index, + type: type, compression: compression, } ); @@ -1443,7 +1443,7 @@ THREE.EXRLoader.prototype = Object.assign( Object.create( THREE.DataTextureLoade array: info.array, offset: inOffset, size: dwaHeader.dcCompressedSize - } + }; var dcBuffer = new Uint16Array( uncompressZIP( zlibInfo ).buffer ); inOffset.value += dwaHeader.dcCompressedSize; @@ -1505,20 +1505,22 @@ THREE.EXRLoader.prototype = Object.assign( Object.create( THREE.DataTextureLoade for ( var byte = 0; byte < INT16_SIZE * cd.type; ++ byte ) { - outBuffer[ rowOffsetBytes++ ] = rleBuffer[ rleOffset + byte * cd.width * cd.height ]; + outBuffer[ rowOffsetBytes ++ ] = rleBuffer[ rleOffset + byte * cd.width * cd.height ]; } - rleOffset++ + rleOffset ++; } - row++; + row ++; } break; + case LOSSY_DCT: // skip + default: throw 'EXRLoader.parse: unsupported channel compression'; @@ -1641,45 +1643,60 @@ THREE.EXRLoader.prototype = Object.assign( Object.create( THREE.DataTextureLoade } - // https://stackoverflow.com/questions/32633585/how-do-you-convert-to-half-floats-in-javascript - var encodeFloat16 = ( function() { - - var floatView = new Float32Array(1); - var int32View = new Int32Array(floatView.buffer); - - return function toHalf( fval ) { - - floatView[ 0 ] = fval; - var fbits = int32View[ 0 ]; - var sign = ( fbits >> 16 ) & 0x8000; // sign only - var val = ( fbits & 0x7fffffff ) + 0x1000; // rounded value - - if ( val >= 0x47800000 ) { // might be or become NaN/Inf - if ( ( fbits & 0x7fffffff ) >= 0x47800000 ) { - // is or must become NaN/Inf - if( val < 0x7f800000 ) { // was value but too large - return sign | 0x7c00; // make it +/-Inf - } - return sign | 0x7c00 | // remains +/-Inf or NaN - ( fbits & 0x007fffff ) >> 13; // keep NaN (and Inf) bits + var encodeFloat16 = ( function () { + + // Source: http://gamedev.stackexchange.com/questions/17326/conversion-of-a-number-from-single-precision-floating-point-representation-to-a/17410#17410 + + var floatView = new Float32Array( 1 ); + var int32View = new Int32Array( floatView.buffer ); + + /* This method is faster than the OpenEXR implementation (very often + * used, eg. in Ogre), with the additional benefit of rounding, inspired + * by James Tursa?s half-precision code. */ + return function toHalf( val ) { + + floatView[ 0 ] = val; + var x = int32View[ 0 ]; + + var bits = ( x >> 16 ) & 0x8000; /* Get the sign */ + var m = ( x >> 12 ) & 0x07ff; /* Keep one extra bit for rounding */ + var e = ( x >> 23 ) & 0xff; /* Using int is faster here */ + + /* If zero, or denormal, or exponent underflows too much for a denormal + * half, return signed zero. */ + if ( e < 103 ) return bits; + + /* If NaN, return NaN. If Inf or exponent overflow, return Inf. */ + if ( e > 142 ) { + + bits |= 0x7c00; + /* If exponent was 0xff and one mantissa bit was set, it means NaN, + * not Inf, so make sure we set one mantissa bit too. */ + bits |= ( ( e == 255 ) ? 0 : 1 ) && ( x & 0x007fffff ); + return bits; + + } + + /* If exponent underflows but not too much, return a denormal */ + if ( e < 113 ) { + + m |= 0x0800; + /* Extra rounding may overflow and set mantissa to 0 and exponent + * to 1, which is OK. */ + bits |= ( m >> ( 114 - e ) ) + ( ( m >> ( 113 - e ) ) & 1 ); + return bits; + } - return sign | 0x7bff; // unrounded not quite Inf - } - if ( val >= 0x38800000 ) { // remains normalized value - return sign | val - 0x38000000 >> 13; // exp - 127 + 15 - } - if ( val < 0x33000000 ) { // too small for subnormal - return sign; // becomes +/-0 - } - val = ( fbits & 0x7fffffff ) >> 23; // tmp exp for subnormal calc - return sign | ( ( fbits & 0x7fffff | 0x800000 ) // add subnormal bit - + ( 0x800000 >>> val - 102 ) // round depending on cut off - >> 126 - val ); // div by 2^(1-(exp-127+15)) and >> 13 | exp= - + + bits |= ( ( e - 112 ) << 10 ) | ( m >> 1 ); + /* Extra rounding. An overflow will set mantissa to 0 and increment + * the exponent, which is OK. */ + bits += m & 1; + return bits; + }; - - } () ); + } )(); function parseUint16( dataView, offset ) { @@ -1925,7 +1942,7 @@ THREE.EXRLoader.prototype = Object.assign( Object.create( THREE.DataTextureLoade scanlineBlockSize = 256; uncompress = uncompressDWA; break; - + default: throw 'EXRLoader.parse: ' + EXRHeader.compression + ' is unsupported'; diff --git a/examples/jsm/loaders/EXRLoader.js b/examples/jsm/loaders/EXRLoader.js index 0ac661468a2f44..9147ca39b37370 100644 --- a/examples/jsm/loaders/EXRLoader.js +++ b/examples/jsm/loaders/EXRLoader.js @@ -756,7 +756,7 @@ EXRLoader.prototype = Object.assign( Object.create( DataTextureLoader.prototype var count = - l; size -= count + 1; - + for ( var i = 0; i < count; i ++ ) { out.push( reader.getUint8( p ++ ) ); @@ -768,7 +768,7 @@ EXRLoader.prototype = Object.assign( Object.create( DataTextureLoader.prototype var count = l; size -= 2; - + var value = reader.getUint8( p ++ ); for ( var i = 0; i < count + 1; i ++ ) { @@ -799,7 +799,7 @@ EXRLoader.prototype = Object.assign( Object.create( DataTextureLoader.prototype var numBlocksY = Math.ceil( height / 8.0 ); var leftoverX = width - ( numBlocksX - 1 ) * 8; var leftoverY = height - ( numBlocksY - 1 ) * 8; - + var currAcComp = { value: 0 }; var currDcComp = new Array( numComp ); var dctData = new Array( numComp ); @@ -836,7 +836,7 @@ EXRLoader.prototype = Object.assign( Object.create( DataTextureLoader.prototype halfZigBlock[ comp ].fill( 0 ); // set block DC component - halfZigBlock[ comp ][ 0 ] = dcBuffer[ currDcComp[ comp ]++ ]; + halfZigBlock[ comp ][ 0 ] = dcBuffer[ currDcComp[ comp ] ++ ]; // set block AC components unRleAC( currAcComp, acBuffer, halfZigBlock[ comp ] ); @@ -868,13 +868,13 @@ EXRLoader.prototype = Object.assign( Object.create( DataTextureLoader.prototype let type = channelData[ cscSet.idx[ comp ] ].type; for ( let y = 8 * blocky; y < 8 * blocky + maxY; ++ y ) { - + offset = rowOffsets[ comp ][ y ]; - + for ( let blockx = 0; blockx < numFullBlocksX; ++ blockx ) { - + let src = blockx * 64 + ( ( y & 0x7 ) * 8 ); - + dataView.setUint16( offset + 0 * INT16_SIZE * type, rowBlock[ comp ][ src + 0 ], true ); dataView.setUint16( offset + 1 * INT16_SIZE * type, rowBlock[ comp ][ src + 1 ], true ); dataView.setUint16( offset + 2 * INT16_SIZE * type, rowBlock[ comp ][ src + 2 ], true ); @@ -886,29 +886,29 @@ EXRLoader.prototype = Object.assign( Object.create( DataTextureLoader.prototype dataView.setUint16( offset + 7 * INT16_SIZE * type, rowBlock[ comp ][ src + 7 ], true ); offset += 8 * INT16_SIZE * type; - + } - + } // handle partial X blocks if ( numFullBlocksX != numBlocksX ) { for ( let y = 8 * blocky; y < 8 * blocky + maxY; ++ y ) { - + let offset = rowOffsets[ comp ][ y ] + 8 * numFullBlocksX * INT16_SIZE * type; let src = numFullBlocksX * 64 + ( ( y & 0x7 ) * 8 ); - + for ( let x = 0; x < maxX; ++ x ) { dataView.setUint16( offset + x * INT16_SIZE * type, rowBlock[ comp ][ src + x ], true ); } - + } } - + } // comp } // blocky @@ -966,11 +966,11 @@ EXRLoader.prototype = Object.assign( Object.create( DataTextureLoader.prototype } else { halfZigBlock[ dctComp ] = acValue; - dctComp++; + dctComp ++; } - currAcComp.value++; + currAcComp.value ++; } @@ -978,88 +978,88 @@ EXRLoader.prototype = Object.assign( Object.create( DataTextureLoader.prototype function unZigZag( src, dst ) { - dst[0] = decodeFloat16( src[0] ); - dst[1] = decodeFloat16( src[1] ); - dst[2] = decodeFloat16( src[5] ); - dst[3] = decodeFloat16( src[6] ); - dst[4] = decodeFloat16( src[14] ); - dst[5] = decodeFloat16( src[15] ); - dst[6] = decodeFloat16( src[27] ); - dst[7] = decodeFloat16( src[28] ); - dst[8] = decodeFloat16( src[2] ); - dst[9] = decodeFloat16( src[4] ); - - dst[10] = decodeFloat16( src[7] ); - dst[11] = decodeFloat16( src[13] ); - dst[12] = decodeFloat16( src[16] ); - dst[13] = decodeFloat16( src[26] ); - dst[14] = decodeFloat16( src[29] ); - dst[15] = decodeFloat16( src[42] ); - dst[16] = decodeFloat16( src[3] ); - dst[17] = decodeFloat16( src[8] ); - dst[18] = decodeFloat16( src[12] ); - dst[19] = decodeFloat16( src[17] ); - - dst[20] = decodeFloat16( src[25] ); - dst[21] = decodeFloat16( src[30] ); - dst[22] = decodeFloat16( src[41] ); - dst[23] = decodeFloat16( src[43] ); - dst[24] = decodeFloat16( src[9] ); - dst[25] = decodeFloat16( src[11] ); - dst[26] = decodeFloat16( src[18] ); - dst[27] = decodeFloat16( src[24] ); - dst[28] = decodeFloat16( src[31] ); - dst[29] = decodeFloat16( src[40] ); - - dst[30] = decodeFloat16( src[44] ); - dst[31] = decodeFloat16( src[53] ); - dst[32] = decodeFloat16( src[10] ); - dst[33] = decodeFloat16( src[19] ); - dst[34] = decodeFloat16( src[23] ); - dst[35] = decodeFloat16( src[32] ); - dst[36] = decodeFloat16( src[39] ); - dst[37] = decodeFloat16( src[45] ); - dst[38] = decodeFloat16( src[52] ); - dst[39] = decodeFloat16( src[54] ); - - dst[40] = decodeFloat16( src[20] ); - dst[41] = decodeFloat16( src[22] ); - dst[42] = decodeFloat16( src[33] ); - dst[43] = decodeFloat16( src[38] ); - dst[44] = decodeFloat16( src[46] ); - dst[45] = decodeFloat16( src[51] ); - dst[46] = decodeFloat16( src[55] ); - dst[47] = decodeFloat16( src[60] ); - dst[48] = decodeFloat16( src[21] ); - dst[49] = decodeFloat16( src[34] ); - - dst[50] = decodeFloat16( src[37] ); - dst[51] = decodeFloat16( src[47] ); - dst[52] = decodeFloat16( src[50] ); - dst[53] = decodeFloat16( src[56] ); - dst[54] = decodeFloat16( src[59] ); - dst[55] = decodeFloat16( src[61] ); - dst[56] = decodeFloat16( src[35] ); - dst[57] = decodeFloat16( src[36] ); - dst[58] = decodeFloat16( src[48] ); - dst[59] = decodeFloat16( src[49] ); - - dst[60] = decodeFloat16( src[57] ); - dst[61] = decodeFloat16( src[58] ); - dst[62] = decodeFloat16( src[62] ); - dst[63] = decodeFloat16( src[63] ); + dst[ 0 ] = decodeFloat16( src[ 0 ] ); + dst[ 1 ] = decodeFloat16( src[ 1 ] ); + dst[ 2 ] = decodeFloat16( src[ 5 ] ); + dst[ 3 ] = decodeFloat16( src[ 6 ] ); + dst[ 4 ] = decodeFloat16( src[ 14 ] ); + dst[ 5 ] = decodeFloat16( src[ 15 ] ); + dst[ 6 ] = decodeFloat16( src[ 27 ] ); + dst[ 7 ] = decodeFloat16( src[ 28 ] ); + dst[ 8 ] = decodeFloat16( src[ 2 ] ); + dst[ 9 ] = decodeFloat16( src[ 4 ] ); + + dst[ 10 ] = decodeFloat16( src[ 7 ] ); + dst[ 11 ] = decodeFloat16( src[ 13 ] ); + dst[ 12 ] = decodeFloat16( src[ 16 ] ); + dst[ 13 ] = decodeFloat16( src[ 26 ] ); + dst[ 14 ] = decodeFloat16( src[ 29 ] ); + dst[ 15 ] = decodeFloat16( src[ 42 ] ); + dst[ 16 ] = decodeFloat16( src[ 3 ] ); + dst[ 17 ] = decodeFloat16( src[ 8 ] ); + dst[ 18 ] = decodeFloat16( src[ 12 ] ); + dst[ 19 ] = decodeFloat16( src[ 17 ] ); + + dst[ 20 ] = decodeFloat16( src[ 25 ] ); + dst[ 21 ] = decodeFloat16( src[ 30 ] ); + dst[ 22 ] = decodeFloat16( src[ 41 ] ); + dst[ 23 ] = decodeFloat16( src[ 43 ] ); + dst[ 24 ] = decodeFloat16( src[ 9 ] ); + dst[ 25 ] = decodeFloat16( src[ 11 ] ); + dst[ 26 ] = decodeFloat16( src[ 18 ] ); + dst[ 27 ] = decodeFloat16( src[ 24 ] ); + dst[ 28 ] = decodeFloat16( src[ 31 ] ); + dst[ 29 ] = decodeFloat16( src[ 40 ] ); + + dst[ 30 ] = decodeFloat16( src[ 44 ] ); + dst[ 31 ] = decodeFloat16( src[ 53 ] ); + dst[ 32 ] = decodeFloat16( src[ 10 ] ); + dst[ 33 ] = decodeFloat16( src[ 19 ] ); + dst[ 34 ] = decodeFloat16( src[ 23 ] ); + dst[ 35 ] = decodeFloat16( src[ 32 ] ); + dst[ 36 ] = decodeFloat16( src[ 39 ] ); + dst[ 37 ] = decodeFloat16( src[ 45 ] ); + dst[ 38 ] = decodeFloat16( src[ 52 ] ); + dst[ 39 ] = decodeFloat16( src[ 54 ] ); + + dst[ 40 ] = decodeFloat16( src[ 20 ] ); + dst[ 41 ] = decodeFloat16( src[ 22 ] ); + dst[ 42 ] = decodeFloat16( src[ 33 ] ); + dst[ 43 ] = decodeFloat16( src[ 38 ] ); + dst[ 44 ] = decodeFloat16( src[ 46 ] ); + dst[ 45 ] = decodeFloat16( src[ 51 ] ); + dst[ 46 ] = decodeFloat16( src[ 55 ] ); + dst[ 47 ] = decodeFloat16( src[ 60 ] ); + dst[ 48 ] = decodeFloat16( src[ 21 ] ); + dst[ 49 ] = decodeFloat16( src[ 34 ] ); + + dst[ 50 ] = decodeFloat16( src[ 37 ] ); + dst[ 51 ] = decodeFloat16( src[ 47 ] ); + dst[ 52 ] = decodeFloat16( src[ 50 ] ); + dst[ 53 ] = decodeFloat16( src[ 56 ] ); + dst[ 54 ] = decodeFloat16( src[ 59 ] ); + dst[ 55 ] = decodeFloat16( src[ 61 ] ); + dst[ 56 ] = decodeFloat16( src[ 35 ] ); + dst[ 57 ] = decodeFloat16( src[ 36 ] ); + dst[ 58 ] = decodeFloat16( src[ 48 ] ); + dst[ 59 ] = decodeFloat16( src[ 49 ] ); + + dst[ 60 ] = decodeFloat16( src[ 57 ] ); + dst[ 61 ] = decodeFloat16( src[ 58 ] ); + dst[ 62 ] = decodeFloat16( src[ 62 ] ); + dst[ 63 ] = decodeFloat16( src[ 63 ] ); } function dctInverse( data ) { - const a = .5 * Math.cos ( 3.14159 / 4.0 ); - const b = .5 * Math.cos ( 3.14159 / 16.0 ); - const c = .5 * Math.cos ( 3.14159 / 8.0 ); - const d = .5 * Math.cos ( 3.*3.14159 / 16.0 ); - const e = .5 * Math.cos ( 5.*3.14159 / 16.0 ); - const f = .5 * Math.cos ( 3.*3.14159 / 8.0 ); - const g = .5 * Math.cos ( 7.*3.14159 / 16.0 ); + const a = 0.5 * Math.cos( 3.14159 / 4.0 ); + const b = 0.5 * Math.cos( 3.14159 / 16.0 ); + const c = 0.5 * Math.cos( 3.14159 / 8.0 ); + const d = 0.5 * Math.cos( 3.0 * 3.14159 / 16.0 ); + const e = 0.5 * Math.cos( 5.0 * 3.14159 / 16.0 ); + const f = 0.5 * Math.cos( 3.0 * 3.14159 / 8.0 ); + const g = 0.5 * Math.cos( 7.0 * 3.14159 / 16.0 ); var alpha = new Array( 4 ); var beta = new Array( 4 ); @@ -1070,31 +1070,31 @@ EXRLoader.prototype = Object.assign( Object.create( DataTextureLoader.prototype var rowPtr = row * 8; - alpha[ 0 ] = c * data[ rowPtr + 2 ]; - alpha[ 1 ] = f * data[ rowPtr + 2 ]; - alpha[ 2 ] = c * data[ rowPtr + 6 ]; - alpha[ 3 ] = f * data[ rowPtr + 6 ]; - + alpha[ 0 ] = c * data[ rowPtr + 2 ]; + alpha[ 1 ] = f * data[ rowPtr + 2 ]; + alpha[ 2 ] = c * data[ rowPtr + 6 ]; + alpha[ 3 ] = f * data[ rowPtr + 6 ]; + beta[ 0 ] = b * data[ rowPtr + 1 ] + d * data[ rowPtr + 3 ] + e * data[ rowPtr + 5 ] + g * data[ rowPtr + 7 ]; beta[ 1 ] = d * data[ rowPtr + 1 ] - g * data[ rowPtr + 3 ] - b * data[ rowPtr + 5 ] - e * data[ rowPtr + 7 ]; beta[ 2 ] = e * data[ rowPtr + 1 ] - b * data[ rowPtr + 3 ] + g * data[ rowPtr + 5 ] + d * data[ rowPtr + 7 ]; beta[ 3 ] = g * data[ rowPtr + 1 ] - e * data[ rowPtr + 3 ] + d * data[ rowPtr + 5 ] - b * data[ rowPtr + 7 ]; - + theta[ 0 ] = a * ( data[ rowPtr + 0 ] + data[ rowPtr + 4 ] ); theta[ 3 ] = a * ( data[ rowPtr + 0 ] - data[ rowPtr + 4 ] ); - theta[ 1 ] = alpha[ 0 ] + alpha[ 3 ]; - theta[ 2 ] = alpha[ 1 ] - alpha[ 2 ]; - + theta[ 1 ] = alpha[ 0 ] + alpha[ 3 ]; + theta[ 2 ] = alpha[ 1 ] - alpha[ 2 ]; + gamma[ 0 ] = theta[ 0 ] + theta[ 1 ]; gamma[ 1 ] = theta[ 3 ] + theta[ 2 ]; gamma[ 2 ] = theta[ 3 ] - theta[ 2 ]; gamma[ 3 ] = theta[ 0 ] - theta[ 1 ]; - + data[ rowPtr + 0 ] = gamma[ 0 ] + beta[ 0 ]; data[ rowPtr + 1 ] = gamma[ 1 ] + beta[ 1 ]; data[ rowPtr + 2 ] = gamma[ 2 ] + beta[ 2 ]; data[ rowPtr + 3 ] = gamma[ 3 ] + beta[ 3 ]; - + data[ rowPtr + 4 ] = gamma[ 3 ] - beta[ 3 ]; data[ rowPtr + 5 ] = gamma[ 2 ] - beta[ 2 ]; data[ rowPtr + 6 ] = gamma[ 1 ] - beta[ 1 ]; @@ -1104,29 +1104,29 @@ EXRLoader.prototype = Object.assign( Object.create( DataTextureLoader.prototype for ( var column = 0; column < 8; ++ column ) { - alpha[ 0 ] = c * data[ 16 + column ]; - alpha[ 1 ] = f * data[ 16 + column ]; - alpha[ 2 ] = c * data[ 48 + column ]; - alpha[ 3 ] = f * data[ 48 + column ]; + alpha[ 0 ] = c * data[ 16 + column ]; + alpha[ 1 ] = f * data[ 16 + column ]; + alpha[ 2 ] = c * data[ 48 + column ]; + alpha[ 3 ] = f * data[ 48 + column ]; - beta[ 0 ] = b * data[ 8 + column ] + d * data[ 24 + column ] + e * data[ 40 + column ] + g * data[ 56 + column ]; - beta[ 1 ] = d * data[ 8 + column ] - g * data[ 24 + column ] - b * data[ 40 + column ] - e * data[ 56 + column ]; - beta[ 2 ] = e * data[ 8 + column ] - b * data[ 24 + column ] + g * data[ 40 + column ] + d * data[ 56 + column ]; - beta[ 3 ] = g * data [8 + column ] - e * data[ 24 + column ] + d * data[ 40 + column ] - b * data[ 56 + column ]; + beta[ 0 ] = b * data[ 8 + column ] + d * data[ 24 + column ] + e * data[ 40 + column ] + g * data[ 56 + column ]; + beta[ 1 ] = d * data[ 8 + column ] - g * data[ 24 + column ] - b * data[ 40 + column ] - e * data[ 56 + column ]; + beta[ 2 ] = e * data[ 8 + column ] - b * data[ 24 + column ] + g * data[ 40 + column ] + d * data[ 56 + column ]; + beta[ 3 ] = g * data[ 8 + column ] - e * data[ 24 + column ] + d * data[ 40 + column ] - b * data[ 56 + column ]; theta[ 0 ] = a * ( data[ column ] + data[ 32 + column ] ); theta[ 3 ] = a * ( data[ column ] - data[ 32 + column ] ); - theta[ 1 ] = alpha[ 0 ] + alpha[ 3 ]; - theta[ 2 ] = alpha[ 1 ] - alpha[ 2 ]; + theta[ 1 ] = alpha[ 0 ] + alpha[ 3 ]; + theta[ 2 ] = alpha[ 1 ] - alpha[ 2 ]; gamma[ 0 ] = theta[ 0 ] + theta[ 1 ]; gamma[ 1 ] = theta[ 3 ] + theta[ 2 ]; gamma[ 2 ] = theta[ 3 ] - theta[ 2 ]; gamma[ 3 ] = theta[ 0 ] - theta[ 1 ]; - data[ column ] = gamma[ 0 ] + beta[ 0 ]; - data[ 8 + column ] = gamma[ 1 ] + beta[ 1 ]; + data[ 0 + column ] = gamma[ 0 ] + beta[ 0 ]; + data[ 8 + column ] = gamma[ 1 ] + beta[ 1 ]; data[ 16 + column ] = gamma[ 2 ] + beta[ 2 ]; data[ 24 + column ] = gamma[ 3 ] + beta[ 3 ]; @@ -1345,15 +1345,15 @@ EXRLoader.prototype = Object.assign( Object.create( DataTextureLoader.prototype totalDcUncompressedCount: parseInt64( inDataView, inOffset ), acCompression: parseInt64( inDataView, inOffset ) - } + }; - if ( dwaHeader.version < 2 ) + if ( dwaHeader.version < 2 ) throw 'EXRLoader.parse: ' + EXRHeader.compression + ' version ' + dwaHeader.version + ' is unsupported'; // Read channel ruleset information var channelRules = new Array(); var ruleSize = parseUint16( inDataView, inOffset ) - INT16_SIZE; - + while ( ruleSize > 0 ) { var name = parseNullTerminatedString( inDataView.buffer, inOffset ); @@ -1363,10 +1363,10 @@ EXRLoader.prototype = Object.assign( Object.create( DataTextureLoader.prototype var index = new Int8Array( [ csc ] )[ 0 ]; var type = parseUint8( inDataView, inOffset ); - channelRules.push( { - name: name, - index: index, - type: type, + channelRules.push( { + name: name, + index: index, + type: type, compression: compression, } ); @@ -1454,7 +1454,7 @@ EXRLoader.prototype = Object.assign( Object.create( DataTextureLoader.prototype array: info.array, offset: inOffset, size: dwaHeader.dcCompressedSize - } + }; var dcBuffer = new Uint16Array( uncompressZIP( zlibInfo ).buffer ); inOffset.value += dwaHeader.dcCompressedSize; @@ -1516,20 +1516,22 @@ EXRLoader.prototype = Object.assign( Object.create( DataTextureLoader.prototype for ( var byte = 0; byte < INT16_SIZE * cd.type; ++ byte ) { - outBuffer[ rowOffsetBytes++ ] = rleBuffer[ rleOffset + byte * cd.width * cd.height ]; + outBuffer[ rowOffsetBytes ++ ] = rleBuffer[ rleOffset + byte * cd.width * cd.height ]; } - rleOffset++ + rleOffset ++; } - row++; + row ++; } break; + case LOSSY_DCT: // skip + default: throw 'EXRLoader.parse: unsupported channel compression'; @@ -1652,45 +1654,60 @@ EXRLoader.prototype = Object.assign( Object.create( DataTextureLoader.prototype } - // https://stackoverflow.com/questions/32633585/how-do-you-convert-to-half-floats-in-javascript - var encodeFloat16 = ( function() { - - var floatView = new Float32Array(1); - var int32View = new Int32Array(floatView.buffer); - - return function toHalf( fval ) { - - floatView[ 0 ] = fval; - var fbits = int32View[ 0 ]; - var sign = ( fbits >> 16 ) & 0x8000; // sign only - var val = ( fbits & 0x7fffffff ) + 0x1000; // rounded value - - if ( val >= 0x47800000 ) { // might be or become NaN/Inf - if ( ( fbits & 0x7fffffff ) >= 0x47800000 ) { - // is or must become NaN/Inf - if( val < 0x7f800000 ) { // was value but too large - return sign | 0x7c00; // make it +/-Inf - } - return sign | 0x7c00 | // remains +/-Inf or NaN - ( fbits & 0x007fffff ) >> 13; // keep NaN (and Inf) bits + var encodeFloat16 = ( function () { + + // Source: http://gamedev.stackexchange.com/questions/17326/conversion-of-a-number-from-single-precision-floating-point-representation-to-a/17410#17410 + + var floatView = new Float32Array( 1 ); + var int32View = new Int32Array( floatView.buffer ); + + /* This method is faster than the OpenEXR implementation (very often + * used, eg. in Ogre), with the additional benefit of rounding, inspired + * by James Tursa?s half-precision code. */ + return function toHalf( val ) { + + floatView[ 0 ] = val; + var x = int32View[ 0 ]; + + var bits = ( x >> 16 ) & 0x8000; /* Get the sign */ + var m = ( x >> 12 ) & 0x07ff; /* Keep one extra bit for rounding */ + var e = ( x >> 23 ) & 0xff; /* Using int is faster here */ + + /* If zero, or denormal, or exponent underflows too much for a denormal + * half, return signed zero. */ + if ( e < 103 ) return bits; + + /* If NaN, return NaN. If Inf or exponent overflow, return Inf. */ + if ( e > 142 ) { + + bits |= 0x7c00; + /* If exponent was 0xff and one mantissa bit was set, it means NaN, + * not Inf, so make sure we set one mantissa bit too. */ + bits |= ( ( e == 255 ) ? 0 : 1 ) && ( x & 0x007fffff ); + return bits; + + } + + /* If exponent underflows but not too much, return a denormal */ + if ( e < 113 ) { + + m |= 0x0800; + /* Extra rounding may overflow and set mantissa to 0 and exponent + * to 1, which is OK. */ + bits |= ( m >> ( 114 - e ) ) + ( ( m >> ( 113 - e ) ) & 1 ); + return bits; + } - return sign | 0x7bff; // unrounded not quite Inf - } - if ( val >= 0x38800000 ) { // remains normalized value - return sign | val - 0x38000000 >> 13; // exp - 127 + 15 - } - if ( val < 0x33000000 ) { // too small for subnormal - return sign; // becomes +/-0 - } - val = ( fbits & 0x7fffffff ) >> 23; // tmp exp for subnormal calc - return sign | ( ( fbits & 0x7fffff | 0x800000 ) // add subnormal bit - + ( 0x800000 >>> val - 102 ) // round depending on cut off - >> 126 - val ); // div by 2^(1-(exp-127+15)) and >> 13 | exp= - + + bits |= ( ( e - 112 ) << 10 ) | ( m >> 1 ); + /* Extra rounding. An overflow will set mantissa to 0 and increment + * the exponent, which is OK. */ + bits += m & 1; + return bits; + }; - - } () ); + } )(); function parseUint16( dataView, offset ) { @@ -1936,7 +1953,7 @@ EXRLoader.prototype = Object.assign( Object.create( DataTextureLoader.prototype scanlineBlockSize = 256; uncompress = uncompressDWA; break; - + default: throw 'EXRLoader.parse: ' + EXRHeader.compression + ' is unsupported';