diff --git a/examples/jsm/libs/openjph/libopenjph.wasm b/examples/jsm/libs/openjph/libopenjph.wasm new file mode 100755 index 00000000000000..1b9972639e3aa2 Binary files /dev/null and b/examples/jsm/libs/openjph/libopenjph.wasm differ diff --git a/examples/jsm/libs/openjph/openjph.module.js b/examples/jsm/libs/openjph/openjph.module.js new file mode 100644 index 00000000000000..3e3c18939ece73 --- /dev/null +++ b/examples/jsm/libs/openjph/openjph.module.js @@ -0,0 +1,16 @@ + +var Module = (() => { + var _scriptDir = import.meta.url; + + return ( +function(Module) { + Module = Module || {}; + +var Module=typeof Module!="undefined"?Module:{};var readyPromiseResolve,readyPromiseReject;Module["ready"]=new Promise(function(resolve,reject){readyPromiseResolve=resolve;readyPromiseReject=reject});var moduleOverrides=Object.assign({},Module);var arguments_=[];var thisProgram="./this.program";var quit_=(status,toThrow)=>{throw toThrow};var ENVIRONMENT_IS_WEB=true;var ENVIRONMENT_IS_WORKER=false;var scriptDirectory="";function locateFile(path){if(Module["locateFile"]){return Module["locateFile"](path,scriptDirectory)}return scriptDirectory+path}var read_,readAsync,readBinary,setWindowTitle;if(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER){if(ENVIRONMENT_IS_WORKER){scriptDirectory=self.location.href}else if(typeof document!="undefined"&&document.currentScript){scriptDirectory=document.currentScript.src}if(_scriptDir){scriptDirectory=_scriptDir}if(scriptDirectory.indexOf("blob:")!==0){scriptDirectory=scriptDirectory.substr(0,scriptDirectory.replace(/[?#].*/,"").lastIndexOf("/")+1)}else{scriptDirectory=""}{read_=(url=>{var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.send(null);return xhr.responseText});if(ENVIRONMENT_IS_WORKER){readBinary=(url=>{var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.responseType="arraybuffer";xhr.send(null);return new Uint8Array(xhr.response)})}readAsync=((url,onload,onerror)=>{var xhr=new XMLHttpRequest;xhr.open("GET",url,true);xhr.responseType="arraybuffer";xhr.onload=(()=>{if(xhr.status==200||xhr.status==0&&xhr.response){onload(xhr.response);return}onerror()});xhr.onerror=onerror;xhr.send(null)})}setWindowTitle=(title=>document.title=title)}else{}var out=Module["print"]||console.log.bind(console);var err=Module["printErr"]||console.warn.bind(console);Object.assign(Module,moduleOverrides);moduleOverrides=null;if(Module["arguments"])arguments_=Module["arguments"];if(Module["thisProgram"])thisProgram=Module["thisProgram"];if(Module["quit"])quit_=Module["quit"];var tempRet0=0;var setTempRet0=value=>{tempRet0=value};var getTempRet0=()=>tempRet0;var wasmBinary;if(Module["wasmBinary"])wasmBinary=Module["wasmBinary"];var noExitRuntime=Module["noExitRuntime"]||true;if(typeof WebAssembly!="object"){abort("no native wasm support detected")}var wasmMemory;var ABORT=false;var EXITSTATUS;function getCFunc(ident){var func=Module["_"+ident];return func}function ccall(ident,returnType,argTypes,args,opts){var toC={"string":function(str){var ret=0;if(str!==null&&str!==undefined&&str!==0){var len=(str.length<<2)+1;ret=stackAlloc(len);stringToUTF8(str,ret,len)}return ret},"array":function(arr){var ret=stackAlloc(arr.length);writeArrayToMemory(arr,ret);return ret}};function convertReturnValue(ret){if(returnType==="string")return UTF8ToString(ret);if(returnType==="boolean")return Boolean(ret);return ret}var func=getCFunc(ident);var cArgs=[];var stack=0;if(args){for(var i=0;i=endIdx))++endPtr;if(endPtr-idx>16&&heapOrArray.buffer&&UTF8Decoder){return UTF8Decoder.decode(heapOrArray.subarray(idx,endPtr))}else{var str="";while(idx>10,56320|ch&1023)}}}return str}function UTF8ToString(ptr,maxBytesToRead){return ptr?UTF8ArrayToString(HEAPU8,ptr,maxBytesToRead):""}function stringToUTF8Array(str,heap,outIdx,maxBytesToWrite){if(!(maxBytesToWrite>0))return 0;var startIdx=outIdx;var endIdx=outIdx+maxBytesToWrite-1;for(var i=0;i=55296&&u<=57343){var u1=str.charCodeAt(++i);u=65536+((u&1023)<<10)|u1&1023}if(u<=127){if(outIdx>=endIdx)break;heap[outIdx++]=u}else if(u<=2047){if(outIdx+1>=endIdx)break;heap[outIdx++]=192|u>>6;heap[outIdx++]=128|u&63}else if(u<=65535){if(outIdx+2>=endIdx)break;heap[outIdx++]=224|u>>12;heap[outIdx++]=128|u>>6&63;heap[outIdx++]=128|u&63}else{if(outIdx+3>=endIdx)break;heap[outIdx++]=240|u>>18;heap[outIdx++]=128|u>>12&63;heap[outIdx++]=128|u>>6&63;heap[outIdx++]=128|u&63}}heap[outIdx]=0;return outIdx-startIdx}function stringToUTF8(str,outPtr,maxBytesToWrite){return stringToUTF8Array(str,HEAPU8,outPtr,maxBytesToWrite)}function writeArrayToMemory(array,buffer){HEAP8.set(array,buffer)}var buffer,HEAP8,HEAPU8,HEAP16,HEAPU16,HEAP32,HEAPU32,HEAPF32,HEAPF64;function updateGlobalBufferAndViews(buf){buffer=buf;Module["HEAP8"]=HEAP8=new Int8Array(buf);Module["HEAP16"]=HEAP16=new Int16Array(buf);Module["HEAP32"]=HEAP32=new Int32Array(buf);Module["HEAPU8"]=HEAPU8=new Uint8Array(buf);Module["HEAPU16"]=HEAPU16=new Uint16Array(buf);Module["HEAPU32"]=HEAPU32=new Uint32Array(buf);Module["HEAPF32"]=HEAPF32=new Float32Array(buf);Module["HEAPF64"]=HEAPF64=new Float64Array(buf)}var INITIAL_MEMORY=Module["INITIAL_MEMORY"]||16777216;var wasmTable;var __ATPRERUN__=[];var __ATINIT__=[];var __ATPOSTRUN__=[];var runtimeInitialized=false;function preRun(){if(Module["preRun"]){if(typeof Module["preRun"]=="function")Module["preRun"]=[Module["preRun"]];while(Module["preRun"].length){addOnPreRun(Module["preRun"].shift())}}callRuntimeCallbacks(__ATPRERUN__)}function initRuntime(){runtimeInitialized=true;callRuntimeCallbacks(__ATINIT__)}function postRun(){if(Module["postRun"]){if(typeof Module["postRun"]=="function")Module["postRun"]=[Module["postRun"]];while(Module["postRun"].length){addOnPostRun(Module["postRun"].shift())}}callRuntimeCallbacks(__ATPOSTRUN__)}function addOnPreRun(cb){__ATPRERUN__.unshift(cb)}function addOnInit(cb){__ATINIT__.unshift(cb)}function addOnPostRun(cb){__ATPOSTRUN__.unshift(cb)}var runDependencies=0;var runDependencyWatcher=null;var dependenciesFulfilled=null;function addRunDependency(id){runDependencies++;if(Module["monitorRunDependencies"]){Module["monitorRunDependencies"](runDependencies)}}function removeRunDependency(id){runDependencies--;if(Module["monitorRunDependencies"]){Module["monitorRunDependencies"](runDependencies)}if(runDependencies==0){if(runDependencyWatcher!==null){clearInterval(runDependencyWatcher);runDependencyWatcher=null}if(dependenciesFulfilled){var callback=dependenciesFulfilled;dependenciesFulfilled=null;callback()}}}function abort(what){{if(Module["onAbort"]){Module["onAbort"](what)}}what="Aborted("+what+")";err(what);ABORT=true;EXITSTATUS=1;what+=". Build with -sASSERTIONS for more info.";var e=new WebAssembly.RuntimeError(what);readyPromiseReject(e);throw e}var dataURIPrefix="data:application/octet-stream;base64,";function isDataURI(filename){return filename.startsWith(dataURIPrefix)}var wasmBinaryFile;if(Module["locateFile"]){wasmBinaryFile="libopenjph.wasm";if(!isDataURI(wasmBinaryFile)){wasmBinaryFile=locateFile(wasmBinaryFile)}}else{wasmBinaryFile=new URL("libopenjph.wasm",import.meta.url).toString()}function getBinary(file){try{if(file==wasmBinaryFile&&wasmBinary){return new Uint8Array(wasmBinary)}if(readBinary){return readBinary(file)}else{throw"both async and sync fetching of the wasm failed"}}catch(err){abort(err)}}function getBinaryPromise(){if(!wasmBinary&&(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER)){if(typeof fetch=="function"){return fetch(wasmBinaryFile,{credentials:"same-origin"}).then(function(response){if(!response["ok"]){throw"failed to load wasm binary file at '"+wasmBinaryFile+"'"}return response["arrayBuffer"]()}).catch(function(){return getBinary(wasmBinaryFile)})}}return Promise.resolve().then(function(){return getBinary(wasmBinaryFile)})}function createWasm(){var info={"a":asmLibraryArg};function receiveInstance(instance,module){var exports=instance.exports;Module["asm"]=exports;wasmMemory=Module["asm"]["u"];updateGlobalBufferAndViews(wasmMemory.buffer);wasmTable=Module["asm"]["K"];addOnInit(Module["asm"]["v"]);removeRunDependency("wasm-instantiate")}addRunDependency("wasm-instantiate");function receiveInstantiationResult(result){receiveInstance(result["instance"])}function instantiateArrayBuffer(receiver){return getBinaryPromise().then(function(binary){return WebAssembly.instantiate(binary,info)}).then(function(instance){return instance}).then(receiver,function(reason){err("failed to asynchronously prepare wasm: "+reason);abort(reason)})}function instantiateAsync(){if(!wasmBinary&&typeof WebAssembly.instantiateStreaming=="function"&&!isDataURI(wasmBinaryFile)&&typeof fetch=="function"){return fetch(wasmBinaryFile,{credentials:"same-origin"}).then(function(response){var result=WebAssembly.instantiateStreaming(response,info);return result.then(receiveInstantiationResult,function(reason){err("wasm streaming compile failed: "+reason);err("falling back to ArrayBuffer instantiation");return instantiateArrayBuffer(receiveInstantiationResult)})})}else{return instantiateArrayBuffer(receiveInstantiationResult)}}if(Module["instantiateWasm"]){try{var exports=Module["instantiateWasm"](info,receiveInstance);return exports}catch(e){err("Module.instantiateWasm callback failed with error: "+e);return false}}instantiateAsync().catch(readyPromiseReject);return{}}function callRuntimeCallbacks(callbacks){while(callbacks.length>0){var callback=callbacks.shift();if(typeof callback=="function"){callback(Module);continue}var func=callback.func;if(typeof func=="number"){if(callback.arg===undefined){getWasmTableEntry(func)()}else{getWasmTableEntry(func)(callback.arg)}}else{func(callback.arg===undefined?null:callback.arg)}}}var wasmTableMirror=[];function getWasmTableEntry(funcPtr){var func=wasmTableMirror[funcPtr];if(!func){if(funcPtr>=wasmTableMirror.length)wasmTableMirror.length=funcPtr+1;wasmTableMirror[funcPtr]=func=wasmTable.get(funcPtr)}return func}function ___cxa_allocate_exception(size){return _malloc(size+24)+24}var exceptionCaught=[];function exception_addRef(info){info.add_ref()}var uncaughtExceptionCount=0;function ___cxa_begin_catch(ptr){var info=new ExceptionInfo(ptr);if(!info.get_caught()){info.set_caught(true);uncaughtExceptionCount--}info.set_rethrown(false);exceptionCaught.push(info);exception_addRef(info);return info.get_exception_ptr()}var exceptionLast=0;function ExceptionInfo(excPtr){this.excPtr=excPtr;this.ptr=excPtr-24;this.set_type=function(type){HEAPU32[this.ptr+4>>2]=type};this.get_type=function(){return HEAPU32[this.ptr+4>>2]};this.set_destructor=function(destructor){HEAPU32[this.ptr+8>>2]=destructor};this.get_destructor=function(){return HEAPU32[this.ptr+8>>2]};this.set_refcount=function(refcount){HEAP32[this.ptr>>2]=refcount};this.set_caught=function(caught){caught=caught?1:0;HEAP8[this.ptr+12>>0]=caught};this.get_caught=function(){return HEAP8[this.ptr+12>>0]!=0};this.set_rethrown=function(rethrown){rethrown=rethrown?1:0;HEAP8[this.ptr+13>>0]=rethrown};this.get_rethrown=function(){return HEAP8[this.ptr+13>>0]!=0};this.init=function(type,destructor){this.set_adjusted_ptr(0);this.set_type(type);this.set_destructor(destructor);this.set_refcount(0);this.set_caught(false);this.set_rethrown(false)};this.add_ref=function(){var value=HEAP32[this.ptr>>2];HEAP32[this.ptr>>2]=value+1};this.release_ref=function(){var prev=HEAP32[this.ptr>>2];HEAP32[this.ptr>>2]=prev-1;return prev===1};this.set_adjusted_ptr=function(adjustedPtr){HEAPU32[this.ptr+16>>2]=adjustedPtr};this.get_adjusted_ptr=function(){return HEAPU32[this.ptr+16>>2]};this.get_exception_ptr=function(){var isPointer=___cxa_is_pointer_type(this.get_type());if(isPointer){return HEAPU32[this.excPtr>>2]}var adjusted=this.get_adjusted_ptr();if(adjusted!==0)return adjusted;return this.excPtr}}function ___cxa_free_exception(ptr){return _free(new ExceptionInfo(ptr).ptr)}function exception_decRef(info){if(info.release_ref()&&!info.get_rethrown()){var destructor=info.get_destructor();if(destructor){getWasmTableEntry(destructor)(info.excPtr)}___cxa_free_exception(info.excPtr)}}function ___cxa_end_catch(){_setThrew(0);var info=exceptionCaught.pop();exception_decRef(info);exceptionLast=0}function ___resumeException(ptr){if(!exceptionLast){exceptionLast=ptr}throw ptr}function ___cxa_find_matching_catch_2(){var thrown=exceptionLast;if(!thrown){setTempRet0(0);return 0}var info=new ExceptionInfo(thrown);info.set_adjusted_ptr(thrown);var thrownType=info.get_type();if(!thrownType){setTempRet0(0);return thrown}var typeArray=Array.prototype.slice.call(arguments);for(var i=0;i>>16);updateGlobalBufferAndViews(wasmMemory.buffer);return 1}catch(e){}}function _emscripten_resize_heap(requestedSize){var oldSize=HEAPU8.length;requestedSize=requestedSize>>>0;var maxHeapSize=_emscripten_get_heap_max();if(requestedSize>maxHeapSize){return false}let alignUp=(x,multiple)=>x+(multiple-x%multiple)%multiple;for(var cutDown=1;cutDown<=4;cutDown*=2){var overGrownHeapSize=oldSize*(1+.2/cutDown);overGrownHeapSize=Math.min(overGrownHeapSize,requestedSize+100663296);var newSize=Math.min(maxHeapSize,alignUp(Math.max(requestedSize,overGrownHeapSize),65536));var replacement=emscripten_realloc_buffer(newSize);if(replacement){return true}}return false}var SYSCALLS={varargs:undefined,get:function(){SYSCALLS.varargs+=4;var ret=HEAP32[SYSCALLS.varargs-4>>2];return ret},getStr:function(ptr){var ret=UTF8ToString(ptr);return ret}};function _fd_close(fd){return 52}function _fd_seek(fd,offset_low,offset_high,whence,newOffset){return 70}var printCharBuffers=[null,[],[]];function printChar(stream,curr){var buffer=printCharBuffers[stream];if(curr===0||curr===10){(stream===1?out:err)(UTF8ArrayToString(buffer,0));buffer.length=0}else{buffer.push(curr)}}function _fd_write(fd,iov,iovcnt,pnum){var num=0;for(var i=0;i>2];var len=HEAPU32[iov+4>>2];iov+=8;for(var j=0;j>2]=num;return 0}function _getTempRet0(){return getTempRet0()}function _llvm_eh_typeid_for(type){return type}var asmLibraryArg={"a":___cxa_allocate_exception,"k":___cxa_begin_catch,"j":___cxa_end_catch,"f":___cxa_find_matching_catch_2,"m":___cxa_find_matching_catch_3,"r":___cxa_free_exception,"b":___cxa_throw,"e":___resumeException,"h":_abort,"q":_emscripten_memcpy_big,"o":_emscripten_resize_heap,"p":_fd_close,"n":_fd_seek,"i":_fd_write,"c":_getTempRet0,"g":invoke_ii,"s":invoke_iii,"t":invoke_viiiiii,"d":invoke_viiiiiii,"l":_llvm_eh_typeid_for};var asm=createWasm();var ___wasm_call_ctors=Module["___wasm_call_ctors"]=function(){return(___wasm_call_ctors=Module["___wasm_call_ctors"]=Module["asm"]["v"]).apply(null,arguments)};var _create_j2c_data=Module["_create_j2c_data"]=function(){return(_create_j2c_data=Module["_create_j2c_data"]=Module["asm"]["w"]).apply(null,arguments)};var _init_j2c_data=Module["_init_j2c_data"]=function(){return(_init_j2c_data=Module["_init_j2c_data"]=Module["asm"]["x"]).apply(null,arguments)};var _get_j2c_width=Module["_get_j2c_width"]=function(){return(_get_j2c_width=Module["_get_j2c_width"]=Module["asm"]["y"]).apply(null,arguments)};var _get_j2c_height=Module["_get_j2c_height"]=function(){return(_get_j2c_height=Module["_get_j2c_height"]=Module["asm"]["z"]).apply(null,arguments)};var _get_j2c_bit_depth=Module["_get_j2c_bit_depth"]=function(){return(_get_j2c_bit_depth=Module["_get_j2c_bit_depth"]=Module["asm"]["A"]).apply(null,arguments)};var _get_j2c_is_signed=Module["_get_j2c_is_signed"]=function(){return(_get_j2c_is_signed=Module["_get_j2c_is_signed"]=Module["asm"]["B"]).apply(null,arguments)};var _get_j2c_num_components=Module["_get_j2c_num_components"]=function(){return(_get_j2c_num_components=Module["_get_j2c_num_components"]=Module["asm"]["C"]).apply(null,arguments)};var _get_j2c_downsampling_x=Module["_get_j2c_downsampling_x"]=function(){return(_get_j2c_downsampling_x=Module["_get_j2c_downsampling_x"]=Module["asm"]["D"]).apply(null,arguments)};var _get_j2c_downsampling_y=Module["_get_j2c_downsampling_y"]=function(){return(_get_j2c_downsampling_y=Module["_get_j2c_downsampling_y"]=Module["asm"]["E"]).apply(null,arguments)};var _parse_j2c_data=Module["_parse_j2c_data"]=function(){return(_parse_j2c_data=Module["_parse_j2c_data"]=Module["asm"]["F"]).apply(null,arguments)};var _restrict_input_resolution=Module["_restrict_input_resolution"]=function(){return(_restrict_input_resolution=Module["_restrict_input_resolution"]=Module["asm"]["G"]).apply(null,arguments)};var _enable_resilience=Module["_enable_resilience"]=function(){return(_enable_resilience=Module["_enable_resilience"]=Module["asm"]["H"]).apply(null,arguments)};var _pull_j2c_line=Module["_pull_j2c_line"]=function(){return(_pull_j2c_line=Module["_pull_j2c_line"]=Module["asm"]["I"]).apply(null,arguments)};var _release_j2c_data=Module["_release_j2c_data"]=function(){return(_release_j2c_data=Module["_release_j2c_data"]=Module["asm"]["J"]).apply(null,arguments)};var _free=Module["_free"]=function(){return(_free=Module["_free"]=Module["asm"]["L"]).apply(null,arguments)};var _malloc=Module["_malloc"]=function(){return(_malloc=Module["_malloc"]=Module["asm"]["M"]).apply(null,arguments)};var _setThrew=Module["_setThrew"]=function(){return(_setThrew=Module["_setThrew"]=Module["asm"]["N"]).apply(null,arguments)};var stackSave=Module["stackSave"]=function(){return(stackSave=Module["stackSave"]=Module["asm"]["O"]).apply(null,arguments)};var stackRestore=Module["stackRestore"]=function(){return(stackRestore=Module["stackRestore"]=Module["asm"]["P"]).apply(null,arguments)};var stackAlloc=Module["stackAlloc"]=function(){return(stackAlloc=Module["stackAlloc"]=Module["asm"]["Q"]).apply(null,arguments)};var ___cxa_can_catch=Module["___cxa_can_catch"]=function(){return(___cxa_can_catch=Module["___cxa_can_catch"]=Module["asm"]["R"]).apply(null,arguments)};var ___cxa_is_pointer_type=Module["___cxa_is_pointer_type"]=function(){return(___cxa_is_pointer_type=Module["___cxa_is_pointer_type"]=Module["asm"]["S"]).apply(null,arguments)};function invoke_ii(index,a1){var sp=stackSave();try{return getWasmTableEntry(index)(a1)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viiiiiii(index,a1,a2,a3,a4,a5,a6,a7){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6,a7)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viiiiii(index,a1,a2,a3,a4,a5,a6){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_iii(index,a1,a2){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}Module["ccall"]=ccall;Module["cwrap"]=cwrap;Module["writeArrayToMemory"]=writeArrayToMemory;var calledRun;dependenciesFulfilled=function runCaller(){if(!calledRun)run();if(!calledRun)dependenciesFulfilled=runCaller};function run(args){args=args||arguments_;if(runDependencies>0){return}preRun();if(runDependencies>0){return}function doRun(){if(calledRun)return;calledRun=true;Module["calledRun"]=true;if(ABORT)return;initRuntime();readyPromiseResolve(Module);if(Module["onRuntimeInitialized"])Module["onRuntimeInitialized"]();postRun()}if(Module["setStatus"]){Module["setStatus"]("Running...");setTimeout(function(){setTimeout(function(){Module["setStatus"]("")},1);doRun()},1)}else{doRun()}}Module["run"]=run;if(Module["preInit"]){if(typeof Module["preInit"]=="function")Module["preInit"]=[Module["preInit"]];while(Module["preInit"].length>0){Module["preInit"].pop()()}}run(); + + + return Module.ready +} +); +})(); +export default Module; \ No newline at end of file diff --git a/examples/jsm/loaders/EXRLoader.js b/examples/jsm/loaders/EXRLoader.js index 2307151b0aaacf..5276439b732689 100644 --- a/examples/jsm/loaders/EXRLoader.js +++ b/examples/jsm/loaders/EXRLoader.js @@ -10,6 +10,9 @@ import { RGBAFormat } from 'three'; import * as fflate from '../libs/fflate.module.js'; +import OpenJPHModule from '../libs/openjph/openjph.module.js'; + +let _openjph; // Referred to the original Industrial Light & Magic OpenEXR implementation and the TinyEXR / Syoyo Fujita // implementation, so I have preserved their copyright notices. @@ -84,11 +87,15 @@ import * as fflate from '../libs/fflate.module.js'; * A loader for the OpenEXR texture format. * * `EXRLoader` currently supports uncompressed, ZIP(S), RLE, PIZ and DWA/B compression. + * HTJ2K (High Throughput JPEG 2000) compression is supported when the OpenJPH WASM module is loaded. * Supports reading as UnsignedByte, HalfFloat and Float type data texture. * * ```js * const loader = new EXRLoader(); * const texture = await loader.loadAsync( 'textures/memorial.exr' ); + * + * // For HTJ2K support: + * EXRLoader.setOpenJPH( openjphModule ); * ``` * * @augments DataTextureLoader @@ -129,7 +136,7 @@ class EXRLoader extends DataTextureLoader { * @param {ArrayBuffer} buffer - The raw texture data. * @return {DataTextureLoader~TexData} An object representing the parsed texture data. */ - parse( buffer ) { + async parse( buffer ) { const USHORT_RANGE = ( 1 << 16 ); const BITMAP_SIZE = ( USHORT_RANGE >> 3 ); @@ -1777,7 +1784,164 @@ class EXRLoader extends DataTextureLoader { } - function parseNullTerminatedString( buffer, offset ) { + async function uncompressHTJ2K( info ) { + + // HTJ2K (High Throughput JPEG 2000) decompression + // Reference: ISO/IEC 15444-15 / ITU-T T.814 + + // Lazy initialization of OpenJPH WASM module + let openjph; + + if ( ! _openjph ) { + + _openjph = new Promise( async ( resolve ) => { + + const openjph = await OpenJPHModule(); + resolve( openjph ); + + } ); + + } + + openjph = await _openjph; + + const compressedData = info.array; + const wasm = openjph; + const dv = new DataView( compressedData.buffer, compressedData.byteOffset, compressedData.byteLength ); + + // Parse HTJ2K header + let offset = 0; + + // Read magic number (0x4854 = 'HT') + const magic = dv.getUint16( offset, false ); // big-endian + offset += 2; + + if ( magic !== 0x4854 ) { + + throw new Error( + `THREE.EXRLoader: Invalid HTJ2K magic number: 0x${magic.toString( 16 )}. Expected 0x4854.` + ); + + } + + // Read payload length + offset += 4; // skip payloadLength field (unused) + + // Read number of channels + const numChannels = dv.getUint16( offset, false ); // big-endian + offset += 2; + + // Read channel map + const channelMap = new Uint16Array( numChannels ); + for ( let i = 0; i < numChannels; i ++ ) { + + channelMap[ i ] = dv.getUint16( offset, false ); // big-endian + offset += 2; + + } + + const headerSize = offset; + + // Extract JPEG 2000 codestream (data after header) + const codestreamSize = compressedData.byteLength - headerSize; + const codestreamData = new Uint8Array( compressedData.buffer, compressedData.byteOffset + headerSize, codestreamSize ); + + // Create J2K data structure + const j2kData = wasm._create_j2c_data(); + + if ( ! j2kData ) { + + throw new Error( 'THREE.EXRLoader: Failed to create J2K data structure.' ); + + } + + try { + + // Allocate memory for codestream + const dataPtr = wasm._malloc( codestreamSize ); + wasm.HEAPU8.set( codestreamData, dataPtr ); + + // Initialize codestream + wasm._init_j2c_data( j2kData, dataPtr, codestreamSize ); + + // Free input data + wasm._free( dataPtr ); + + // Parse codestream + wasm._parse_j2c_data( j2kData ); + + // Get dimensions from codestream + const width = wasm._get_width( j2kData ); + const height = wasm._get_height( j2kData ); + const numComponents = wasm._get_num_components( j2kData ); + + // Determine bytes per element from EXR info + const bytesPerElement = info.type === 1 ? 2 : 4; // HALF=1, FLOAT=2 + const totalBytes = width * height * numChannels * bytesPerElement; + + // Allocate output buffer + const outputBuffer = new ArrayBuffer( totalBytes ); + const outputView = new DataView( outputBuffer ); + + // Decode scanlines + let outputOffset = 0; + + for ( let y = 0; y < height; y ++ ) { + + for ( let c = 0; c < numComponents; c ++ ) { + + const linePtr = wasm._pull_j2c_line( j2kData ); + + if ( ! linePtr ) { + + throw new Error( `THREE.EXRLoader: Failed to decode HTJ2K line ${y}, component ${c}.` ); + + } + + // Copy and convert line data + for ( let x = 0; x < width; x ++ ) { + + const value = wasm.HEAP32[ ( linePtr >> 2 ) + x ]; + + if ( bytesPerElement === 2 ) { + + // HALF float - store as uint16 + outputView.setUint16( outputOffset, value, true ); + outputOffset += 2; + + } else { + + // FLOAT - store as int32 (will be reinterpreted as float) + outputView.setInt32( outputOffset, value, true ); + outputOffset += 4; + + } + + } + + } + + } + + // Release J2K data + wasm._release_j2c_data( j2kData ); + + return outputView; + + } catch ( error ) { + + // Clean up on error + if ( j2kData ) { + + wasm._release_j2c_data( j2kData ); + + } + + throw error; + + } + + } function parseNullTerminatedString( buffer, offset ) { const uintBuffer = new Uint8Array( buffer ); let endOffset = 0; @@ -1995,7 +2159,9 @@ class EXRLoader extends DataTextureLoader { 'B44_COMPRESSION', 'B44A_COMPRESSION', 'DWAA_COMPRESSION', - 'DWAB_COMPRESSION' + 'DWAB_COMPRESSION', + 'UNKNOWN_COMPRESSION', // 10 + 'HTJ2K_COMPRESSION' // 11 - High Throughput JPEG 2000 (ISO/IEC 15444-15) ]; const compression = parseUint8( dataView, offset ); @@ -2210,7 +2376,7 @@ class EXRLoader extends DataTextureLoader { } - function parseTiles() { + async function parseTiles() { const EXRDecoder = this; const offset = EXRDecoder.offset; @@ -2230,7 +2396,7 @@ class EXRLoader extends DataTextureLoader { const bytesBlockLine = EXRDecoder.columns * EXRDecoder.totalBytes; const isCompressed = EXRDecoder.size < EXRDecoder.lines * bytesBlockLine; - const viewer = isCompressed ? EXRDecoder.uncompress( EXRDecoder ) : uncompressRAW( EXRDecoder ); + const viewer = isCompressed ? await EXRDecoder.uncompress( EXRDecoder ) : uncompressRAW( EXRDecoder ); offset.value += EXRDecoder.size; @@ -2264,7 +2430,7 @@ class EXRLoader extends DataTextureLoader { } - function parseScanline() { + async function parseScanline() { const EXRDecoder = this; const offset = EXRDecoder.offset; @@ -2278,7 +2444,7 @@ class EXRLoader extends DataTextureLoader { const bytesPerLine = EXRDecoder.columns * EXRDecoder.totalBytes; const isCompressed = EXRDecoder.size < EXRDecoder.lines * bytesPerLine; - const viewer = isCompressed ? EXRDecoder.uncompress( EXRDecoder ) : uncompressRAW( EXRDecoder ); + const viewer = isCompressed ? await EXRDecoder.uncompress( EXRDecoder ) : uncompressRAW( EXRDecoder ); offset.value += EXRDecoder.size; @@ -2447,6 +2613,11 @@ class EXRLoader extends DataTextureLoader { EXRDecoder.uncompress = uncompressDWA; break; + case 'HTJ2K_COMPRESSION': + EXRDecoder.blockHeight = 32; // Typical block height for HTJ2K, may vary + EXRDecoder.uncompress = uncompressHTJ2K; + break; + default: throw new Error( 'EXRLoader.parse: ' + EXRHeader.compression + ' is unsupported' ); @@ -2702,7 +2873,7 @@ class EXRLoader extends DataTextureLoader { const EXRDecoder = setupDecoder( EXRHeader, bufferDataView, uInt8Array, offset, this.type, this.outputFormat ); // parse input data - EXRDecoder.decode(); + await EXRDecoder.decode(); // output texture post-processing if ( EXRDecoder.shouldExpand ) { diff --git a/examples/jsm/loaders/JPHLoader.js b/examples/jsm/loaders/JPHLoader.js new file mode 100644 index 00000000000000..78f07f6c66b9f7 --- /dev/null +++ b/examples/jsm/loaders/JPHLoader.js @@ -0,0 +1,284 @@ +import { + DataTexture, + FileLoader, + FloatType, + HalfFloatType, + LinearFilter, + LinearSRGBColorSpace, + Loader +} from 'three'; +import OpenJPHModule from '../libs/openjph/openjph.module.js'; + +let _openjph; + +/** + * Loader for JPEG 2000 codestreams using OpenJPH (High-Throughput JPEG 2000). + * + * This loader wraps the OpenJPH WebAssembly module to decode HTJ2K (High Throughput JPEG 2000) + * compressed image data. HTJ2K is defined in ISO/IEC 15444-15 / ITU-T T.814. + * + * Features: + * - Supports lossless and lossy JPEG 2000 compression + * - Handles 8-bit, 16-bit, and 32-bit data + * - Multiple component/channel support + * - Optional resolution reduction for faster decoding + * + * Usage: + * ```javascript + * import { JPHLoader } from 'three/examples/jsm/loaders/JPHLoader.js'; + * + * // Initialize with OpenJPH WASM module + * const loader = new JPHLoader(); + * + * // Load WASM module first (required) + * await loader.setWASMModule(openjphModule); + * + * // Load a J2K/JP2 file + * loader.load('texture.j2k', (texture) => { + * // Use texture + * }); + * + * // Or decode from buffer directly + * const texture = loader.parse(arrayBuffer); + * ``` + * + * OpenJPH WASM Module: + * The OpenJPH library must be compiled to WebAssembly using Emscripten. + * See: https://github.com/aous72/OpenJPH/tree/main/subprojects/js + * + * Build instructions: + * ```bash + * git clone https://github.com/aous72/OpenJPH.git + * cd OpenJPH + * mkdir build && cd build + * emcmake cmake -DCMAKE_BUILD_TYPE=Release .. + * emmake make + * ``` + * + * @author mrdoob / http://mrdoob.com/ + */ + +class JPHLoader extends Loader { + + constructor( manager ) { + + super( manager ); + + this.type = FloatType; + + } + + load( url, onLoad, onProgress, onError ) { + + const scope = this; + + const loader = new FileLoader( this.manager ); + loader.setPath( this.path ); + loader.setResponseType( 'arraybuffer' ); + loader.setRequestHeader( this.requestHeader ); + loader.setWithCredentials( this.withCredentials ); + + loader.load( url, function ( buffer ) { + + try { + + onLoad( scope.parse( buffer ) ); + + } catch ( e ) { + + if ( onError ) { + + onError( e ); + + } else { + + console.error( e ); + + } + + scope.manager.itemError( url ); + + } + + }, onProgress, onError ); + + } + + /** + * Parses JPEG 2000 codestream data and returns a DataTexture. + * + * @param {ArrayBuffer} buffer - The J2K/JP2 file data + * @param {Object} options - Decoding options + * @param {number} options.skipResolutionsRead - Number of resolutions to skip during read (default: 0) + * @param {number} options.skipResolutionsRecon - Number of resolutions to skip during reconstruction (default: 0) + * @returns {DataTexture} The decoded texture + */ + async parse( buffer, options = {} ) { + + // Lazy initialization of OpenJPH WASM module + let openjph; + + if ( ! _openjph ) { + + _openjph = new Promise( async ( resolve ) => { + + const openjph = await OpenJPHModule(); + resolve( openjph ); + + } ); + + } + + openjph = await _openjph; + + const { + skipResolutionsRead = 0, + skipResolutionsRecon = 0 + } = options; + + const wasm = openjph; + + // Create J2K data structure + const j2kData = wasm._create_j2c_data(); + + if ( ! j2kData ) { + + throw new Error( 'THREE.JPHLoader: Failed to create J2K data structure.' ); + + } + + try { + + // Allocate memory for input data + const dataPtr = wasm._malloc( buffer.byteLength ); + wasm.HEAPU8.set( new Uint8Array( buffer ), dataPtr ); + + // Initialize codestream + wasm._init_j2c_data( j2kData, dataPtr, buffer.byteLength ); + + // Free input data + wasm._free( dataPtr ); + + // Get codestream info + const width = wasm._get_width( j2kData ); + const height = wasm._get_height( j2kData ); + const numComponents = wasm._get_num_components( j2kData ); + const bitDepth = wasm._get_bit_depth( j2kData, 0 ); + const isSigned = wasm._is_signed( j2kData, 0 ); + + // Apply resolution reduction if requested + if ( skipResolutionsRead > 0 || skipResolutionsRecon > 0 ) { + + wasm._restrict_input_resolution( j2kData, skipResolutionsRead, skipResolutionsRecon ); + + } + + // Parse and prepare for decoding + wasm._parse_j2c_data( j2kData ); + + // Determine output data type + let outputData; + let textureType; + + if ( bitDepth <= 8 ) { + + // 8-bit output + outputData = new Uint8Array( width * height * numComponents ); + textureType = this.type; // Use default type + + } else if ( bitDepth <= 16 ) { + + // 16-bit output - use Half or Float + outputData = new Uint16Array( width * height * numComponents ); + textureType = HalfFloatType; + + } else { + + // 32-bit float output + outputData = new Float32Array( width * height * numComponents ); + textureType = FloatType; + + } + + // Decode scanlines + let offset = 0; + + for ( let y = 0; y < height; y ++ ) { + + for ( let c = 0; c < numComponents; c ++ ) { + + const linePtr = wasm._pull_j2c_line( j2kData ); + + if ( ! linePtr ) { + + throw new Error( `THREE.JPHLoader: Failed to decode line ${y}, component ${c}.` ); + + } + + // Copy line data + for ( let x = 0; x < width; x ++ ) { + + const value = wasm.HEAP32[ ( linePtr >> 2 ) + x ]; + + // Convert signed to unsigned if needed + if ( isSigned ) { + + outputData[ offset ++ ] = value + ( 1 << ( bitDepth - 1 ) ); + + } else { + + outputData[ offset ++ ] = value; + + } + + } + + } + + } + + // Release J2K data + wasm._release_j2c_data( j2kData ); + + // Create and configure texture + const texture = new DataTexture( outputData, width, height ); + texture.type = textureType; + texture.minFilter = LinearFilter; + texture.magFilter = LinearFilter; + texture.generateMipmaps = false; + texture.needsUpdate = true; + + // Set color space based on number of components + if ( numComponents >= 3 ) { + + texture.colorSpace = LinearSRGBColorSpace; + + } + + return texture; + + } catch ( error ) { + + // Clean up on error + if ( j2kData ) { + + wasm._release_j2c_data( j2kData ); + + } + + throw error; + + } + + } + + setDataType( value ) { + + this.type = value; + return this; + + } + +} + +export { JPHLoader }; diff --git a/examples/webgl_loader_exr_htj2k.html b/examples/webgl_loader_exr_htj2k.html new file mode 100644 index 00000000000000..29012b14450146 --- /dev/null +++ b/examples/webgl_loader_exr_htj2k.html @@ -0,0 +1,180 @@ + + + + three.js webgl - loaders - EXR with HTJ2K compression + + + + + + +
+ three.js - + EXR loader with HTJ2K compression support +
+ + + + + + + diff --git a/examples/webgl_loader_jph.html b/examples/webgl_loader_jph.html new file mode 100644 index 00000000000000..01f843a1eedbc0 --- /dev/null +++ b/examples/webgl_loader_jph.html @@ -0,0 +1,109 @@ + + + + three.js webgl - loaders - JPEG 2000 (HTJ2K) loader + + + + + + +
+ three.js - + OpenJPH JPEG 2000 (HTJ2K) loader +
+ + + + + + +