UNPKG

@loaders.gl/textures

Version:

Framework-independent loaders for compressed and super compressed (basis) textures

81 lines (80 loc) 2.73 kB
// loaders.gl // SPDX-License-Identifier: MIT // Copyright (c) vis.gl contributors const a = new Uint32Array([0x12345678]); const b = new Uint8Array(a.buffer, a.byteOffset, a.byteLength); const isLittleEndian = !(b[0] === 0x12); const LITTLE_ENDIAN_OS = isLittleEndian; /** * The basic string format consists of 3 characters: * 1. a character describing the byteorder of the data (<: little-endian, >: big-endian, |: not-relevant) * 2. a character code giving the basic type of the array * 3. an integer providing the number of bytes the type uses. * https://numpy.org/doc/stable/reference/arrays.interface.html * * Here I only include the second and third characters, and check endianness separately */ const DTYPES = { u1: Uint8Array, i1: Int8Array, u2: Uint16Array, i2: Int16Array, u4: Uint32Array, i4: Int32Array, f4: Float32Array, f8: Float64Array }; export function parseNPY(arrayBuffer, options) { const view = new DataView(arrayBuffer); const { header, headerEndOffset } = parseHeader(view); const numpyType = header.descr; const ArrayType = DTYPES[numpyType.slice(1, 3)]; if (!ArrayType) { throw new Error(`Unimplemented type ${numpyType}`); } const nArrayElements = header.shape?.reduce((a, b) => a * b); const arrayByteLength = nArrayElements * ArrayType.BYTES_PER_ELEMENT; if (arrayBuffer.byteLength < headerEndOffset + arrayByteLength) { throw new Error('Buffer overflow'); } const data = new ArrayType(arrayBuffer.slice(headerEndOffset, headerEndOffset + arrayByteLength)); // Swap endianness if needed if ((numpyType[0] === '>' && LITTLE_ENDIAN_OS) || (numpyType[0] === '<' && !LITTLE_ENDIAN_OS)) { throw new Error('Incorrect endianness'); } return { data, header }; } /** * Parse NPY header * * @param view * @return */ function parseHeader(view) { const majorVersion = view.getUint8(6); // const minorVersion = view.getUint8(7); let offset = 8; let headerLength; if (majorVersion >= 2) { headerLength = view.getUint32(offset, true); offset += 4; } else { headerLength = view.getUint16(offset, true); offset += 2; } const encoding = majorVersion <= 2 ? 'latin1' : 'utf-8'; const decoder = new TextDecoder(encoding); const headerArray = new Uint8Array(view.buffer, offset, headerLength); const headerText = decoder.decode(headerArray); offset += headerLength; const header = JSON.parse(headerText .replace(/'/g, '"') .replace('False', 'false') .replace('(', '[') .replace(/,*\),*/g, ']')); return { header, headerEndOffset: offset }; }