123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225 |
- import { PanoramaToCubeMapTools } from "./panoramaToCubemap.js";
- /**
- * This groups tools to convert HDR texture to native colors array.
- */
- export class HDRTools {
- static _Ldexp(mantissa, exponent) {
- if (exponent > 1023) {
- return mantissa * Math.pow(2, 1023) * Math.pow(2, exponent - 1023);
- }
- if (exponent < -1074) {
- return mantissa * Math.pow(2, -1074) * Math.pow(2, exponent + 1074);
- }
- return mantissa * Math.pow(2, exponent);
- }
- static _Rgbe2float(float32array, red, green, blue, exponent, index) {
- if (exponent > 0) {
- /*nonzero pixel*/
- exponent = this._Ldexp(1.0, exponent - (128 + 8));
- float32array[index + 0] = red * exponent;
- float32array[index + 1] = green * exponent;
- float32array[index + 2] = blue * exponent;
- }
- else {
- float32array[index + 0] = 0;
- float32array[index + 1] = 0;
- float32array[index + 2] = 0;
- }
- }
- static _ReadStringLine(uint8array, startIndex) {
- let line = "";
- let character = "";
- for (let i = startIndex; i < uint8array.length - startIndex; i++) {
- character = String.fromCharCode(uint8array[i]);
- if (character == "\n") {
- break;
- }
- line += character;
- }
- return line;
- }
- /**
- * Reads header information from an RGBE texture stored in a native array.
- * More information on this format are available here:
- * https://en.wikipedia.org/wiki/RGBE_image_format
- *
- * @param uint8array The binary file stored in native array.
- * @returns The header information.
- */
- // eslint-disable-next-line @typescript-eslint/naming-convention
- static RGBE_ReadHeader(uint8array) {
- let height = 0;
- let width = 0;
- let line = this._ReadStringLine(uint8array, 0);
- if (line[0] != "#" || line[1] != "?") {
- // eslint-disable-next-line no-throw-literal
- throw "Bad HDR Format.";
- }
- let endOfHeader = false;
- let findFormat = false;
- let lineIndex = 0;
- do {
- lineIndex += line.length + 1;
- line = this._ReadStringLine(uint8array, lineIndex);
- if (line == "FORMAT=32-bit_rle_rgbe") {
- findFormat = true;
- }
- else if (line.length == 0) {
- endOfHeader = true;
- }
- } while (!endOfHeader);
- if (!findFormat) {
- // eslint-disable-next-line no-throw-literal
- throw "HDR Bad header format, unsupported FORMAT";
- }
- lineIndex += line.length + 1;
- line = this._ReadStringLine(uint8array, lineIndex);
- const sizeRegexp = /^-Y (.*) \+X (.*)$/g;
- const match = sizeRegexp.exec(line);
- // TODO. Support +Y and -X if needed.
- if (!match || match.length < 3) {
- // eslint-disable-next-line no-throw-literal
- throw "HDR Bad header format, no size";
- }
- width = parseInt(match[2]);
- height = parseInt(match[1]);
- if (width < 8 || width > 0x7fff) {
- // eslint-disable-next-line no-throw-literal
- throw "HDR Bad header format, unsupported size";
- }
- lineIndex += line.length + 1;
- return {
- height: height,
- width: width,
- dataPosition: lineIndex,
- };
- }
- /**
- * Returns the cubemap information (each faces texture data) extracted from an RGBE texture.
- * This RGBE texture needs to store the information as a panorama.
- *
- * More information on this format are available here:
- * https://en.wikipedia.org/wiki/RGBE_image_format
- *
- * @param buffer The binary file stored in an array buffer.
- * @param size The expected size of the extracted cubemap.
- * @param supersample enable supersampling the cubemap (default: false)
- * @returns The Cube Map information.
- */
- static GetCubeMapTextureData(buffer, size, supersample = false) {
- const uint8array = new Uint8Array(buffer);
- const hdrInfo = this.RGBE_ReadHeader(uint8array);
- const data = this.RGBE_ReadPixels(uint8array, hdrInfo);
- const cubeMapData = PanoramaToCubeMapTools.ConvertPanoramaToCubemap(data, hdrInfo.width, hdrInfo.height, size, supersample);
- return cubeMapData;
- }
- /**
- * Returns the pixels data extracted from an RGBE texture.
- * This pixels will be stored left to right up to down in the R G B order in one array.
- *
- * More information on this format are available here:
- * https://en.wikipedia.org/wiki/RGBE_image_format
- *
- * @param uint8array The binary file stored in an array buffer.
- * @param hdrInfo The header information of the file.
- * @returns The pixels data in RGB right to left up to down order.
- */
- // eslint-disable-next-line @typescript-eslint/naming-convention
- static RGBE_ReadPixels(uint8array, hdrInfo) {
- return this._RGBEReadPixelsRLE(uint8array, hdrInfo);
- }
- static _RGBEReadPixelsRLE(uint8array, hdrInfo) {
- let num_scanlines = hdrInfo.height;
- const scanline_width = hdrInfo.width;
- let a, b, c, d, count;
- let dataIndex = hdrInfo.dataPosition;
- let index = 0, endIndex = 0, i = 0;
- const scanLineArrayBuffer = new ArrayBuffer(scanline_width * 4); // four channel R G B E
- const scanLineArray = new Uint8Array(scanLineArrayBuffer);
- // 3 channels of 4 bytes per pixel in float.
- const resultBuffer = new ArrayBuffer(hdrInfo.width * hdrInfo.height * 4 * 3);
- const resultArray = new Float32Array(resultBuffer);
- // read in each successive scanline
- while (num_scanlines > 0) {
- a = uint8array[dataIndex++];
- b = uint8array[dataIndex++];
- c = uint8array[dataIndex++];
- d = uint8array[dataIndex++];
- if (a != 2 || b != 2 || c & 0x80 || hdrInfo.width < 8 || hdrInfo.width > 32767) {
- return this._RGBEReadPixelsNOTRLE(uint8array, hdrInfo);
- }
- if (((c << 8) | d) != scanline_width) {
- // eslint-disable-next-line no-throw-literal
- throw "HDR Bad header format, wrong scan line width";
- }
- index = 0;
- // read each of the four channels for the scanline into the buffer
- for (i = 0; i < 4; i++) {
- endIndex = (i + 1) * scanline_width;
- while (index < endIndex) {
- a = uint8array[dataIndex++];
- b = uint8array[dataIndex++];
- if (a > 128) {
- // a run of the same value
- count = a - 128;
- if (count == 0 || count > endIndex - index) {
- // eslint-disable-next-line no-throw-literal
- throw "HDR Bad Format, bad scanline data (run)";
- }
- while (count-- > 0) {
- scanLineArray[index++] = b;
- }
- }
- else {
- // a non-run
- count = a;
- if (count == 0 || count > endIndex - index) {
- // eslint-disable-next-line no-throw-literal
- throw "HDR Bad Format, bad scanline data (non-run)";
- }
- scanLineArray[index++] = b;
- if (--count > 0) {
- for (let j = 0; j < count; j++) {
- scanLineArray[index++] = uint8array[dataIndex++];
- }
- }
- }
- }
- }
- // now convert data from buffer into floats
- for (i = 0; i < scanline_width; i++) {
- a = scanLineArray[i];
- b = scanLineArray[i + scanline_width];
- c = scanLineArray[i + 2 * scanline_width];
- d = scanLineArray[i + 3 * scanline_width];
- this._Rgbe2float(resultArray, a, b, c, d, (hdrInfo.height - num_scanlines) * scanline_width * 3 + i * 3);
- }
- num_scanlines--;
- }
- return resultArray;
- }
- static _RGBEReadPixelsNOTRLE(uint8array, hdrInfo) {
- // this file is not run length encoded
- // read values sequentially
- let num_scanlines = hdrInfo.height;
- const scanline_width = hdrInfo.width;
- let a, b, c, d, i;
- let dataIndex = hdrInfo.dataPosition;
- // 3 channels of 4 bytes per pixel in float.
- const resultBuffer = new ArrayBuffer(hdrInfo.width * hdrInfo.height * 4 * 3);
- const resultArray = new Float32Array(resultBuffer);
- // read in each successive scanline
- while (num_scanlines > 0) {
- for (i = 0; i < hdrInfo.width; i++) {
- a = uint8array[dataIndex++];
- b = uint8array[dataIndex++];
- c = uint8array[dataIndex++];
- d = uint8array[dataIndex++];
- this._Rgbe2float(resultArray, a, b, c, d, (hdrInfo.height - num_scanlines) * scanline_width * 3 + i * 3);
- }
- num_scanlines--;
- }
- return resultArray;
- }
- }
- //# sourceMappingURL=hdr.js.map
|