UNPKG

@babylonjs/viewer

Version:

The Babylon Viewer aims to simplify a specific but common Babylon.js use case: loading, viewing, and interacting with a 3D model.

1,250 lines (1,242 loc) 66 kB
import { aS as FromHalfFloat, aT as ToHalfFloat, j as Clamp, cc as MaxHalfFloat, y as Logger, aY as Tools, C as Constants } from './index-MZPybX0H.esm.js'; const INT32_SIZE = 4; const FLOAT32_SIZE = 4; const INT8_SIZE = 1; const INT16_SIZE = 2; const ULONG_SIZE = 8; const USHORT_RANGE = 1 << 16; const BITMAP_SIZE = USHORT_RANGE >> 3; const HUF_ENCBITS = 16; const HUF_DECBITS = 14; const HUF_ENCSIZE = (1 << HUF_ENCBITS) + 1; const HUF_DECSIZE = 1 << HUF_DECBITS; const HUF_DECMASK = HUF_DECSIZE - 1; const SHORT_ZEROCODE_RUN = 59; const LONG_ZEROCODE_RUN = 63; const SHORTEST_LONG_RUN = 2 + LONG_ZEROCODE_RUN - SHORT_ZEROCODE_RUN; /** * Inspired by https://github.com/sciecode/three.js/blob/dev/examples/jsm/loaders/EXRLoader.js * Referred to the original Industrial Light & Magic OpenEXR implementation and the TinyEXR / Syoyo Fujita * implementation. */ // /* // Copyright (c) 2014 - 2017, Syoyo Fujita // All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are met: // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above copyright // notice, this list of conditions and the following disclaimer in the // documentation and/or other materials provided with the distribution. // * Neither the name of the Syoyo Fujita nor the // names of its contributors may be used to endorse or promote products // derived from this software without specific prior written permission. // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE // DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // */ // // TinyEXR contains some OpenEXR code, which is licensed under ------------ // /////////////////////////////////////////////////////////////////////////// // // // // Copyright (c) 2002, Industrial Light & Magic, a division of Lucas // // Digital Ltd. LLC // // // // All rights reserved. // // // // Redistribution and use in source and binary forms, with or without // // modification, are permitted provided that the following conditions are // // met: // // * Redistributions of source code must retain the above copyright // // notice, this list of conditions and the following disclaimer. // // * Redistributions in binary form must reproduce the above // // copyright notice, this list of conditions and the following disclaimer // // in the documentation and/or other materials provided with the // // distribution. // // * Neither the name of Industrial Light & Magic nor the names of // // its contributors may be used to endorse or promote products derived // // from this software without specific prior written permission. // // // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // // // /////////////////////////////////////////////////////////////////////////// // // End of OpenEXR license ------------------------------------------------- var CompressionCodes; (function (CompressionCodes) { CompressionCodes[CompressionCodes["NO_COMPRESSION"] = 0] = "NO_COMPRESSION"; CompressionCodes[CompressionCodes["RLE_COMPRESSION"] = 1] = "RLE_COMPRESSION"; CompressionCodes[CompressionCodes["ZIPS_COMPRESSION"] = 2] = "ZIPS_COMPRESSION"; CompressionCodes[CompressionCodes["ZIP_COMPRESSION"] = 3] = "ZIP_COMPRESSION"; CompressionCodes[CompressionCodes["PIZ_COMPRESSION"] = 4] = "PIZ_COMPRESSION"; CompressionCodes[CompressionCodes["PXR24_COMPRESSION"] = 5] = "PXR24_COMPRESSION"; })(CompressionCodes || (CompressionCodes = {})); var LineOrders; (function (LineOrders) { LineOrders[LineOrders["INCREASING_Y"] = 0] = "INCREASING_Y"; LineOrders[LineOrders["DECREASING_Y"] = 1] = "DECREASING_Y"; })(LineOrders || (LineOrders = {})); /** * Parse a null terminated string from the buffer * @param buffer buffer to read from * @param offset current offset in the buffer * @returns a string */ function ParseNullTerminatedString(buffer, offset) { const uintBuffer = new Uint8Array(buffer); let endOffset = 0; while (uintBuffer[offset.value + endOffset] != 0) { endOffset += 1; } const stringValue = new TextDecoder().decode(uintBuffer.slice(offset.value, offset.value + endOffset)); offset.value = offset.value + endOffset + 1; return stringValue; } /** * Parse an int32 from the buffer * @param dataView dataview on the data * @param offset current offset in the data view * @returns an int32 */ function ParseInt32(dataView, offset) { const value = dataView.getInt32(offset.value, true); offset.value += INT32_SIZE; return value; } /** * Parse an uint32 from the buffer * @param dataView data view to read from * @param offset offset in the data view * @returns an uint32 */ function ParseUint32(dataView, offset) { const value = dataView.getUint32(offset.value, true); offset.value += INT32_SIZE; return value; } /** * Parse an uint8 from the buffer * @param dataView dataview on the data * @param offset current offset in the data view * @returns an uint8 */ function ParseUint8(dataView, offset) { const value = dataView.getUint8(offset.value); offset.value += INT8_SIZE; return value; } /** * Parse an uint16 from the buffer * @param dataView dataview on the data * @param offset current offset in the data view * @returns an uint16 */ function ParseUint16(dataView, offset) { const value = dataView.getUint16(offset.value, true); offset.value += INT16_SIZE; return value; } /** * Parse an uint8 from an array buffer * @param array array buffer * @param offset current offset in the data view * @returns an uint16 */ function ParseUint8Array(array, offset) { const value = array[offset.value]; offset.value += INT8_SIZE; return value; } /** * Parse an int64 from the buffer * @param dataView dataview on the data * @param offset current offset in the data view * @returns an int64 */ function ParseInt64(dataView, offset) { let int; if ("getBigInt64" in DataView.prototype) { int = Number(dataView.getBigInt64(offset.value, true)); } else { int = dataView.getUint32(offset.value + 4, true) + Number(dataView.getUint32(offset.value, true) << 32); } offset.value += ULONG_SIZE; return int; } /** * Parse a float32 from the buffer * @param dataView dataview on the data * @param offset current offset in the data view * @returns a float32 */ function ParseFloat32(dataView, offset) { const value = dataView.getFloat32(offset.value, true); offset.value += FLOAT32_SIZE; return value; } /** * Parse a float16 from the buffer * @param dataView dataview on the data * @param offset current offset in the data view * @returns a float16 */ function ParseFloat16(dataView, offset) { return FromHalfFloat(ParseUint16(dataView, offset)); } /** * Decode a float32 from the buffer * @param dataView dataview on the data * @param offset current offset in the data view * @returns a float16 */ function DecodeFloat32(dataView, offset) { // EXR FLOAT channels are HDR and can exceed the half-float range. Clamp to the largest finite half return ToHalfFloat(Clamp(ParseFloat32(dataView, offset), -MaxHalfFloat, MaxHalfFloat)); } function ParseFixedLengthString(buffer, offset, size) { const stringValue = new TextDecoder().decode(new Uint8Array(buffer).slice(offset.value, offset.value + size)); offset.value = offset.value + size; return stringValue; } function ParseRational(dataView, offset) { const x = ParseInt32(dataView, offset); const y = ParseUint32(dataView, offset); return [x, y]; } function ParseTimecode(dataView, offset) { const x = ParseUint32(dataView, offset); const y = ParseUint32(dataView, offset); return [x, y]; } function ParseV2f(dataView, offset) { const x = ParseFloat32(dataView, offset); const y = ParseFloat32(dataView, offset); return [x, y]; } function ParseV3f(dataView, offset) { const x = ParseFloat32(dataView, offset); const y = ParseFloat32(dataView, offset); const z = ParseFloat32(dataView, offset); return [x, y, z]; } function ParseChlist(dataView, offset, size) { const startOffset = offset.value; const channels = []; while (offset.value < startOffset + size - 1) { const name = ParseNullTerminatedString(dataView.buffer, offset); const pixelType = ParseInt32(dataView, offset); const pLinear = ParseUint8(dataView, offset); offset.value += 3; // reserved, three chars const xSampling = ParseInt32(dataView, offset); const ySampling = ParseInt32(dataView, offset); channels.push({ name: name, pixelType: pixelType, pLinear: pLinear, xSampling: xSampling, ySampling: ySampling, }); } offset.value += 1; return channels; } function ParseChromaticities(dataView, offset) { const redX = ParseFloat32(dataView, offset); const redY = ParseFloat32(dataView, offset); const greenX = ParseFloat32(dataView, offset); const greenY = ParseFloat32(dataView, offset); const blueX = ParseFloat32(dataView, offset); const blueY = ParseFloat32(dataView, offset); const whiteX = ParseFloat32(dataView, offset); const whiteY = ParseFloat32(dataView, offset); return { redX: redX, redY: redY, greenX: greenX, greenY: greenY, blueX: blueX, blueY: blueY, whiteX: whiteX, whiteY: whiteY }; } function ParseCompression(dataView, offset) { return ParseUint8(dataView, offset); } function ParseBox2i(dataView, offset) { const xMin = ParseInt32(dataView, offset); const yMin = ParseInt32(dataView, offset); const xMax = ParseInt32(dataView, offset); const yMax = ParseInt32(dataView, offset); return { xMin: xMin, yMin: yMin, xMax: xMax, yMax: yMax }; } function ParseLineOrder(dataView, offset) { const lineOrder = ParseUint8(dataView, offset); return LineOrders[lineOrder]; } /** * Parse a value from the data view * @param dataView defines the data view to read from * @param offset defines the current offset in the data view * @param type defines the type of the value to read * @param size defines the size of the value to read * @returns the parsed value */ function ParseValue(dataView, offset, type, size) { switch (type) { case "string": case "stringvector": case "iccProfile": return ParseFixedLengthString(dataView.buffer, offset, size); case "chlist": return ParseChlist(dataView, offset, size); case "chromaticities": return ParseChromaticities(dataView, offset); case "compression": return ParseCompression(dataView, offset); case "box2i": return ParseBox2i(dataView, offset); case "lineOrder": return ParseLineOrder(dataView, offset); case "float": return ParseFloat32(dataView, offset); case "v2f": return ParseV2f(dataView, offset); case "v3f": return ParseV3f(dataView, offset); case "int": return ParseInt32(dataView, offset); case "rational": return ParseRational(dataView, offset); case "timecode": return ParseTimecode(dataView, offset); case "preview": offset.value += size; return "skipped"; default: offset.value += size; return undefined; } } /** * Revert the endianness of the data * @param source defines the source */ function Predictor(source) { for (let t = 1; t < source.length; t++) { const d = source[t - 1] + source[t] - 128; source[t] = d; } } /** * Interleave pixels * @param source defines the data source * @param out defines the output */ function InterleaveScalar(source, out) { let t1 = 0; let t2 = Math.floor((source.length + 1) / 2); let s = 0; const stop = source.length - 1; // eslint-disable-next-line no-constant-condition while (true) { if (s > stop) { break; } out[s++] = source[t1++]; if (s > stop) { break; } out[s++] = source[t2++]; } } /* eslint-disable @typescript-eslint/naming-convention */ /** * Inspired by https://github.com/sciecode/three.js/blob/dev/examples/jsm/loaders/EXRLoader.js * Referred to the original Industrial Light & Magic OpenEXR implementation and the TinyEXR / Syoyo Fujita * implementation. */ // /* // Copyright (c) 2014 - 2017, Syoyo Fujita // All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are met: // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above copyright // notice, this list of conditions and the following disclaimer in the // documentation and/or other materials provided with the distribution. // * Neither the name of the Syoyo Fujita nor the // names of its contributors may be used to endorse or promote products // derived from this software without specific prior written permission. // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE // DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // */ // // TinyEXR contains some OpenEXR code, which is licensed under ------------ // /////////////////////////////////////////////////////////////////////////// // // // // Copyright (c) 2002, Industrial Light & Magic, a division of Lucas // // Digital Ltd. LLC // // // // All rights reserved. // // // // Redistribution and use in source and binary forms, with or without // // modification, are permitted provided that the following conditions are // // met: // // * Redistributions of source code must retain the above copyright // // notice, this list of conditions and the following disclaimer. // // * Redistributions in binary form must reproduce the above // // copyright notice, this list of conditions and the following disclaimer // // in the documentation and/or other materials provided with the // // distribution. // // * Neither the name of Industrial Light & Magic nor the names of // // its contributors may be used to endorse or promote products derived // // from this software without specific prior written permission. // // // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // // // /////////////////////////////////////////////////////////////////////////// // // End of OpenEXR license ------------------------------------------------- const EXR_MAGIC = 20000630; /** * Gets the EXR header * @param dataView defines the data view to read from * @param offset defines the offset to start reading from * @returns the header */ function GetExrHeader(dataView, offset) { if (dataView.getUint32(0, true) != EXR_MAGIC) { throw new Error("Incorrect OpenEXR format"); } const version = dataView.getUint8(4); const specData = dataView.getUint8(5); // fullMask const spec = { singleTile: !!(specData & 2), longName: !!(specData & 4), deepFormat: !!(specData & 8), multiPart: !!(specData & 16), }; offset.value = 8; const headerData = {}; let keepReading = true; while (keepReading) { const attributeName = ParseNullTerminatedString(dataView.buffer, offset); if (!attributeName) { keepReading = false; } else { const attributeType = ParseNullTerminatedString(dataView.buffer, offset); const attributeSize = ParseUint32(dataView, offset); const attributeValue = ParseValue(dataView, offset, attributeType, attributeSize); if (attributeValue === undefined) { Logger.Warn(`Unknown header attribute type ${attributeType}'.`); } else { headerData[attributeName] = attributeValue; } } } if ((specData & -5) != 0) { throw new Error("Unsupported file format"); } return { version: version, spec: spec, ...headerData }; } /** * Inspired by https://github.com/sciecode/three.js/blob/dev/examples/jsm/loaders/EXRLoader.js * Referred to the original Industrial Light & Magic OpenEXR implementation and the TinyEXR / Syoyo Fujita * implementation. */ // /* // Copyright (c) 2014 - 2017, Syoyo Fujita // All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are met: // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above copyright // notice, this list of conditions and the following disclaimer in the // documentation and/or other materials provided with the distribution. // * Neither the name of the Syoyo Fujita nor the // names of its contributors may be used to endorse or promote products // derived from this software without specific prior written permission. // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE // DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // */ // // TinyEXR contains some OpenEXR code, which is licensed under ------------ // /////////////////////////////////////////////////////////////////////////// // // // // Copyright (c) 2002, Industrial Light & Magic, a division of Lucas // // Digital Ltd. LLC // // // // All rights reserved. // // // // Redistribution and use in source and binary forms, with or without // // modification, are permitted provided that the following conditions are // // met: // // * Redistributions of source code must retain the above copyright // // notice, this list of conditions and the following disclaimer. // // * Redistributions in binary form must reproduce the above // // copyright notice, this list of conditions and the following disclaimer // // in the documentation and/or other materials provided with the // // distribution. // // * Neither the name of Industrial Light & Magic nor the names of // // its contributors may be used to endorse or promote products derived // // from this software without specific prior written permission. // // // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // // // /////////////////////////////////////////////////////////////////////////// // // End of OpenEXR license ------------------------------------------------- const NBITS = 16; const A_OFFSET = 1 << (NBITS - 1); const MOD_MASK = (1 << NBITS) - 1; /** @internal */ function ReverseLutFromBitmap(bitmap, lut) { let k = 0; for (let i = 0; i < USHORT_RANGE; ++i) { if (i == 0 || bitmap[i >> 3] & (1 << (i & 7))) { lut[k++] = i; } } const n = k - 1; while (k < USHORT_RANGE) { lut[k++] = 0; } return n; } function HufClearDecTable(hdec) { for (let i = 0; i < HUF_DECSIZE; i++) { hdec[i] = {}; hdec[i].len = 0; hdec[i].lit = 0; hdec[i].p = null; } } function GetBits(nBits, c, lc, array, offset) { while (lc < nBits) { c = (c << 8) | ParseUint8Array(array, offset); lc += 8; } lc -= nBits; return { l: (c >> lc) & ((1 << nBits) - 1), c, lc, }; } function GetChar(c, lc, array, offset) { c = (c << 8) | ParseUint8Array(array, offset); lc += 8; return { c, lc, }; } function GetCode(po, rlc, c, lc, array, offset, outBuffer, outBufferOffset, outBufferEndOffset) { if (po == rlc) { if (lc < 8) { const gc = GetChar(c, lc, array, offset); c = gc.c; lc = gc.lc; } lc -= 8; let cs = c >> lc; cs = new Uint8Array([cs])[0]; if (outBufferOffset.value + cs > outBufferEndOffset) { return null; } const s = outBuffer[outBufferOffset.value - 1]; while (cs-- > 0) { outBuffer[outBufferOffset.value++] = s; } } else if (outBufferOffset.value < outBufferEndOffset) { outBuffer[outBufferOffset.value++] = po; } else { return null; } return { c, lc }; } const HufTableBuffer = new Array(59); function HufCanonicalCodeTable(hcode) { for (let i = 0; i <= 58; ++i) { HufTableBuffer[i] = 0; } for (let i = 0; i < HUF_ENCSIZE; ++i) { HufTableBuffer[hcode[i]] += 1; } let c = 0; for (let i = 58; i > 0; --i) { const nc = (c + HufTableBuffer[i]) >> 1; HufTableBuffer[i] = c; c = nc; } for (let i = 0; i < HUF_ENCSIZE; ++i) { const l = hcode[i]; if (l > 0) { hcode[i] = l | (HufTableBuffer[l]++ << 6); } } } function HufUnpackEncTable(array, offset, ni, im, iM, hcode) { const p = offset; let c = 0; let lc = 0; for (; im <= iM; im++) { if (p.value - offset.value > ni) { return; } let gb = GetBits(6, c, lc, array, p); const l = gb.l; c = gb.c; lc = gb.lc; hcode[im] = l; if (l == LONG_ZEROCODE_RUN) { if (p.value - offset.value > ni) { throw new Error("Error in HufUnpackEncTable"); } gb = GetBits(8, c, lc, array, p); let zerun = gb.l + SHORTEST_LONG_RUN; c = gb.c; lc = gb.lc; if (im + zerun > iM + 1) { throw new Error("Error in HufUnpackEncTable"); } while (zerun--) { hcode[im++] = 0; } im--; } else if (l >= SHORT_ZEROCODE_RUN) { let zerun = l - SHORT_ZEROCODE_RUN + 2; if (im + zerun > iM + 1) { throw new Error("Error in HufUnpackEncTable"); } while (zerun--) { hcode[im++] = 0; } im--; } } HufCanonicalCodeTable(hcode); } function HufLength(code) { return code & 63; } function HufCode(code) { return code >> 6; } function HufBuildDecTable(hcode, im, iM, hdecod) { for (; im <= iM; im++) { const c = HufCode(hcode[im]); const l = HufLength(hcode[im]); if (c >> l) { throw new Error("Invalid table entry"); } if (l > HUF_DECBITS) { const pl = hdecod[c >> (l - HUF_DECBITS)]; if (pl.len) { throw new Error("Invalid table entry"); } pl.lit++; if (pl.p) { const p = pl.p; pl.p = new Array(pl.lit); for (let i = 0; i < pl.lit - 1; ++i) { pl.p[i] = p[i]; } } else { pl.p = new Array(1); } pl.p[pl.lit - 1] = im; } else if (l) { let plOffset = 0; for (let i = 1 << (HUF_DECBITS - l); i > 0; i--) { const pl = hdecod[(c << (HUF_DECBITS - l)) + plOffset]; if (pl.len || pl.p) { throw new Error("Invalid table entry"); } pl.len = l; pl.lit = im; plOffset++; } } } return true; } function HufDecode(encodingTable, decodingTable, array, offset, ni, rlc, no, outBuffer, outOffset) { let c = 0; let lc = 0; const outBufferEndOffset = no; const inOffsetEnd = Math.trunc(offset.value + (ni + 7) / 8); while (offset.value < inOffsetEnd) { let gc = GetChar(c, lc, array, offset); c = gc.c; lc = gc.lc; while (lc >= HUF_DECBITS) { const index = (c >> (lc - HUF_DECBITS)) & HUF_DECMASK; const pl = decodingTable[index]; if (pl.len) { lc -= pl.len; const gCode = GetCode(pl.lit, rlc, c, lc, array, offset, outBuffer, outOffset, outBufferEndOffset); if (gCode) { c = gCode.c; lc = gCode.lc; } } else { if (!pl.p) { throw new Error("hufDecode issues"); } let j; for (j = 0; j < pl.lit; j++) { const l = HufLength(encodingTable[pl.p[j]]); while (lc < l && offset.value < inOffsetEnd) { gc = GetChar(c, lc, array, offset); c = gc.c; lc = gc.lc; } if (lc >= l) { if (HufCode(encodingTable[pl.p[j]]) == ((c >> (lc - l)) & ((1 << l) - 1))) { lc -= l; const gCode = GetCode(pl.p[j], rlc, c, lc, array, offset, outBuffer, outOffset, outBufferEndOffset); if (gCode) { c = gCode.c; lc = gCode.lc; } break; } } } if (j == pl.lit) { throw new Error("HufDecode issues"); } } } } const i = (8 - ni) & 7; c >>= i; lc -= i; while (lc > 0) { const pl = decodingTable[(c << (HUF_DECBITS - lc)) & HUF_DECMASK]; if (pl.len) { lc -= pl.len; const gCode = GetCode(pl.lit, rlc, c, lc, array, offset, outBuffer, outOffset, outBufferEndOffset); if (gCode) { c = gCode.c; lc = gCode.lc; } } else { throw new Error("HufDecode issues"); } } return true; } /** @internal */ function HufUncompress(array, dataView, offset, nCompressed, outBuffer, nRaw) { const outOffset = { value: 0 }; const initialInOffset = offset.value; const im = ParseUint32(dataView, offset); const iM = ParseUint32(dataView, offset); offset.value += 4; const nBits = ParseUint32(dataView, offset); offset.value += 4; if (im < 0 || im >= HUF_ENCSIZE || iM < 0 || iM >= HUF_ENCSIZE) { throw new Error("Wrong HUF_ENCSIZE"); } const freq = new Array(HUF_ENCSIZE); const hdec = new Array(HUF_DECSIZE); HufClearDecTable(hdec); const ni = nCompressed - (offset.value - initialInOffset); HufUnpackEncTable(array, offset, ni, im, iM, freq); if (nBits > 8 * (nCompressed - (offset.value - initialInOffset))) { throw new Error("Wrong hufUncompress"); } HufBuildDecTable(freq, im, iM, hdec); HufDecode(freq, hdec, array, offset, nBits, iM, nRaw, outBuffer, outOffset); } function UInt16(value) { return value & 0xffff; } function Int16(value) { const ref = UInt16(value); return ref > 0x7fff ? ref - 0x10000 : ref; } function Wdec14(l, h) { const ls = Int16(l); const hs = Int16(h); const hi = hs; const ai = ls + (hi & 1) + (hi >> 1); const as = ai; const bs = ai - hi; return { a: as, b: bs }; } function Wdec16(l, h) { const m = UInt16(l); const d = UInt16(h); const bb = (m - (d >> 1)) & MOD_MASK; const aa = (d + bb - A_OFFSET) & MOD_MASK; return { a: aa, b: bb }; } /** @internal */ function Wav2Decode(buffer, j, nx, ox, ny, oy, mx) { const w14 = mx < 1 << 14; const n = nx > ny ? ny : nx; let p = 1; let p2; let py; while (p <= n) { p <<= 1; } p >>= 1; p2 = p; p >>= 1; while (p >= 1) { py = 0; const ey = py + oy * (ny - p2); const oy1 = oy * p; const oy2 = oy * p2; const ox1 = ox * p; const ox2 = ox * p2; let i00, i01, i10, i11; for (; py <= ey; py += oy2) { let px = py; const ex = py + ox * (nx - p2); for (; px <= ex; px += ox2) { const p01 = px + ox1; const p10 = px + oy1; const p11 = p10 + ox1; if (w14) { let result = Wdec14(buffer[px + j], buffer[p10 + j]); i00 = result.a; i10 = result.b; result = Wdec14(buffer[p01 + j], buffer[p11 + j]); i01 = result.a; i11 = result.b; result = Wdec14(i00, i01); buffer[px + j] = result.a; buffer[p01 + j] = result.b; result = Wdec14(i10, i11); buffer[p10 + j] = result.a; buffer[p11 + j] = result.b; } else { let result = Wdec16(buffer[px + j], buffer[p10 + j]); i00 = result.a; i10 = result.b; result = Wdec16(buffer[p01 + j], buffer[p11 + j]); i01 = result.a; i11 = result.b; result = Wdec16(i00, i01); buffer[px + j] = result.a; buffer[p01 + j] = result.b; result = Wdec16(i10, i11); buffer[p10 + j] = result.a; buffer[p11 + j] = result.b; } } if (nx & p) { const p10 = px + oy1; let result; if (w14) { result = Wdec14(buffer[px + j], buffer[p10 + j]); } else { result = Wdec16(buffer[px + j], buffer[p10 + j]); } i00 = result.a; buffer[p10 + j] = result.b; buffer[px + j] = i00; } } if (ny & p) { let px = py; const ex = py + ox * (nx - p2); for (; px <= ex; px += ox2) { const p01 = px + ox1; let result; if (w14) { result = Wdec14(buffer[px + j], buffer[p01 + j]); } else { result = Wdec16(buffer[px + j], buffer[p01 + j]); } i00 = result.a; buffer[p01 + j] = result.b; buffer[px + j] = i00; } } p2 = p; p >>= 1; } return py; } /** @internal */ function ApplyLut(lut, data, nData) { for (let i = 0; i < nData; ++i) { data[i] = lut[data[i]]; } } /** * Inspired by https://github.com/sciecode/three.js/blob/dev/examples/jsm/loaders/EXRLoader.js * Referred to the original Industrial Light & Magic OpenEXR implementation and the TinyEXR / Syoyo Fujita * implementation. */ // /* // Copyright (c) 2014 - 2017, Syoyo Fujita // All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are met: // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above copyright // notice, this list of conditions and the following disclaimer in the // documentation and/or other materials provided with the distribution. // * Neither the name of the Syoyo Fujita nor the // names of its contributors may be used to endorse or promote products // derived from this software without specific prior written permission. // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE // DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // */ // // TinyEXR contains some OpenEXR code, which is licensed under ------------ // /////////////////////////////////////////////////////////////////////////// // // // // Copyright (c) 2002, Industrial Light & Magic, a division of Lucas // // Digital Ltd. LLC // // // // All rights reserved. // // // // Redistribution and use in source and binary forms, with or without // // modification, are permitted provided that the following conditions are // // met: // // * Redistributions of source code must retain the above copyright // // notice, this list of conditions and the following disclaimer. // // * Redistributions in binary form must reproduce the above // // copyright notice, this list of conditions and the following disclaimer // // in the documentation and/or other materials provided with the // // distribution. // // * Neither the name of Industrial Light & Magic nor the names of // // its contributors may be used to endorse or promote products derived // // from this software without specific prior written permission. // // // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // // // /////////////////////////////////////////////////////////////////////////// // // End of OpenEXR license ------------------------------------------------- /** @internal */ function DecodeRunLength(source) { let size = source.byteLength; const out = []; let p = 0; const reader = new DataView(source); while (size > 0) { const l = reader.getInt8(p++); if (l < 0) { const count = -l; size -= count + 1; for (let i = 0; i < count; i++) { out.push(reader.getUint8(p++)); } } else { const count = l; size -= 2; const value = reader.getUint8(p++); for (let i = 0; i < count + 1; i++) { out.push(value); } } } return out; } /** * No compression * @param decoder defines the decoder to use * @returns a decompressed data view */ function UncompressRAW(decoder) { return new DataView(decoder.array.buffer, decoder.offset.value, decoder.size); } /** * RLE compression * @param decoder defines the decoder to use * @returns a decompressed data view */ function UncompressRLE(decoder) { const compressed = decoder.viewer.buffer.slice(decoder.offset.value, decoder.offset.value + decoder.size); const rawBuffer = new Uint8Array(DecodeRunLength(compressed)); const tmpBuffer = new Uint8Array(rawBuffer.length); Predictor(rawBuffer); InterleaveScalar(rawBuffer, tmpBuffer); return new DataView(tmpBuffer.buffer); } /** * Zip compression * @param decoder defines the decoder to use * @returns a decompressed data view */ function UncompressZIP(decoder) { const compressed = decoder.array.slice(decoder.offset.value, decoder.offset.value + decoder.size); const rawBuffer = fflate.unzlibSync(compressed); const tmpBuffer = new Uint8Array(rawBuffer.length); Predictor(rawBuffer); InterleaveScalar(rawBuffer, tmpBuffer); return new DataView(tmpBuffer.buffer); } /** * PXR compression * @param decoder defines the decoder to use * @returns a decompressed data view */ function UncompressPXR(decoder) { const compressed = decoder.array.slice(decoder.offset.value, decoder.offset.value + decoder.size); const rawBuffer = fflate.unzlibSync(compressed); const sz = decoder.lines * decoder.channels * decoder.width; const tmpBuffer = decoder.type == 1 ? new Uint16Array(sz) : new Uint32Array(sz); let tmpBufferEnd = 0; let writePtr = 0; const ptr = new Array(4); for (let y = 0; y < decoder.lines; y++) { for (let c = 0; c < decoder.channels; c++) { let pixel = 0; switch (decoder.type) { case 1: ptr[0] = tmpBufferEnd; ptr[1] = ptr[0] + decoder.width; tmpBufferEnd = ptr[1] + decoder.width; for (let j = 0; j < decoder.width; ++j) { const diff = (rawBuffer[ptr[0]++] << 8) | rawBuffer[ptr[1]++]; pixel += diff; tmpBuffer[writePtr] = pixel; writePtr++; } break; case 2: ptr[0] = tmpBufferEnd; ptr[1] = ptr[0] + decoder.width; ptr[2] = ptr[1] + decoder.width; tmpBufferEnd = ptr[2] + decoder.width; for (let j = 0; j < decoder.width; ++j) { const diff = (rawBuffer[ptr[0]++] << 24) | (rawBuffer[ptr[1]++] << 16) | (rawBuffer[ptr[2]++] << 8); pixel += diff; tmpBuffer[writePtr] = pixel; writePtr++; } break; } } } return new DataView(tmpBuffer.buffer); } /** * PIZ compression * @param decoder defines the decoder to use * @returns a decompressed data view */ function UncompressPIZ(decoder) { const inDataView = decoder.viewer; const inOffset = { value: decoder.offset.value }; const outBuffer = new Uint16Array(decoder.width * decoder.scanlineBlockSize * (decoder.channels * decoder.type)); const bitmap = new Uint8Array(BITMAP_SIZE); // Setup channel info let outBufferEnd = 0; const pizChannelData = new Array(decoder.channels); for (let i = 0; i < decoder.channels; i++) { pizChannelData[i] = {}; pizChannelData[i]["start"] = outBufferEnd; pizChannelData[i]["end"] = pizChannelData[i]["start"]; pizChannelData[i]["nx"] = decoder.width; pizChannelData[i]["ny"] = decoder.lines; pizChannelData[i]["size"] = decoder.type; outBufferEnd += pizChannelData[i].nx * pizChannelData[i].ny * pizChannelData[i].size; } // Read range compression data const minNonZero = ParseUint16(inDataView, inOffset); const maxNonZero = ParseUint16(inDataView, inOffset); if (maxNonZero >= BITMAP_SIZE) { throw new Error("Wrong PIZ_COMPRESSION BITMAP_SIZE"); } if (minNonZero <= maxNonZero) { for (let i = 0; i < maxNonZero - minNonZero + 1; i++) { bitmap[i + minNonZero] = ParseUint8(inDataView, inOffset); } } // Reverse LUT const lut = new Uint16Array(USHORT_RANGE); const maxValue = ReverseLutFromBitmap(bitmap, lut); const length = ParseUint32(inDataView, inOffset); // Huffman decoding HufUncompress(decoder.array, inDataView, inOffset, length, outBuffer, outBufferEnd); // Wavelet decoding for (let i = 0; i < decoder.channels; ++i) { const cd = pizChannelData[i]; for (let j = 0; j < pizChannelData[i].size; ++j) { Wav2Decode(outBuffer, cd.start + j, cd.nx, cd.size, cd.ny, cd.nx * cd.size, maxValue); } } // Expand the pixel data to their original range ApplyLut(lut, outBuffer, outBufferEnd); // Rearrange the pixel data into the format expected by the caller. let tmpOffset = 0; const tmpBuffer = new Uint8Array(outBuffer.buffer.byteLength); for (let y = 0; y < decoder.lines; y++) { for (let c = 0; c < decoder.channels; c++) { const cd = pizChannelData[c]; const n = cd.nx * cd.size; const cp = new Uint8Array(outBuffer.buffer, cd.end * INT16_SIZE, n * INT16_SIZE); tmpBuffer.set(cp, tmpOffset); tmpOffset += n * INT16_SIZE; cd.end += n; } } return new DataView(tmpBuffer.buffer); } var EXROutputType; (function (EXROutputType) { EXROutputType[EXROutputType["Float"] = 0] = "Float"; EXROutputType[EXROutputType["HalfFloat"] = 1] = "HalfFloat"; })(EXROutputType || (EXROutputType = {})); /** * Class used to store configuration of the exr loader */ class ExrLoaderGlobalConfiguration { } /** * Defines the default output type to use (Half float by default) */ ExrLoaderGlobalConfiguration.DefaultOutputType = EXROutputType.HalfFloat; /** * Url to use to load the fflate library (for zip decompression) */ // eslint-disable-next-line @typescript-eslint/naming-convention ExrLoaderGlobalConfiguration.FFLATEUrl = "https://unpkg.com/fflate@0.8.2"; /** * Inspired by https://github.com/sciecode/three.js/blob/dev/examples/jsm/loaders/EXRLoader.js * Referred to the original Industrial Light & Magic OpenEXR implementation and the TinyEXR / Syoyo Fujita * implementation. */ // /* // Copyright (c) 2014 - 2017, Syoyo Fujita // All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are met: // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above copyright // notice, this list of conditions and the following disclaimer in the // documentation and/or other materials provided with the distribution. // * Neither the name of the Syoyo Fujita nor the // names of its contributors may be used to endorse or promote products // derived from this software without specific prior written permission. // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE // DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // */ // // TinyEXR contains some OpenEXR code, which is licensed under ------------ // /////////////////////////////////////////////////////////////////////////// // // // // Copyright (c) 2002, Industrial Light & Magic, a division of Lucas // // Digital Ltd. LLC // // // // All rights reserved. // // // // Redistribution and use in source and binary forms, with or without // // modification, are permitted provided that the following conditions are // // met: // // * Redistributions of source code must retain the above copyright // // notice, this list of conditions and the following disclaimer. // // * Redistributions in binary form must reproduce the above // // copyright notice, this list of conditions and the following disclaimer // // in the documentation and/or other materials provided with the // // distribution. // // * Neither the name of Industrial Light & Magic nor the names of // // its contributors may be used to endorse or promote products derived // // from this software without specific prior written permission. // // // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // // // /////////////////////////////////////////////////////////////////////////// // // End of OpenEXR license ---------------------------