babylon.hdr.ts 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260
  1. module BABYLON {
  2. /**
  3. * Header information of HDR texture files.
  4. */
  5. export interface HDRInfo {
  6. /**
  7. * The height of the texture in pixels.
  8. */
  9. height: number;
  10. /**
  11. * The width of the texture in pixels.
  12. */
  13. width: number;
  14. /**
  15. * The index of the beginning of the data in the binary file.
  16. */
  17. dataPosition: number;
  18. };
  19. /**
  20. * This groups tools to convert HDR texture to native colors array.
  21. */
  22. export class HDRTools {
  23. private static Ldexp(mantissa: number, exponent: number): number {
  24. if (exponent > 1023) {
  25. return mantissa * Math.pow(2, 1023) * Math.pow(2, exponent - 1023);
  26. }
  27. if (exponent < -1074) {
  28. return mantissa * Math.pow(2, -1074) * Math.pow(2, exponent + 1074);
  29. }
  30. return mantissa * Math.pow(2, exponent);
  31. }
  32. private static Rgbe2float(float32array: Float32Array,
  33. red: number, green: number, blue: number, exponent: number,
  34. index: number) {
  35. if (exponent > 0) { /*nonzero pixel*/
  36. exponent = this.Ldexp(1.0, exponent - (128 + 8));
  37. float32array[index + 0] = red * exponent;
  38. float32array[index + 1] = green * exponent;
  39. float32array[index + 2] = blue * exponent;
  40. }
  41. else {
  42. float32array[index + 0] = 0;
  43. float32array[index + 1] = 0;
  44. float32array[index + 2] = 0;
  45. }
  46. }
  47. private static readStringLine(uint8array: Uint8Array, startIndex: number): string {
  48. var line = "";
  49. var character = "";
  50. for (var i = startIndex; i < uint8array.length - startIndex; i++) {
  51. character = String.fromCharCode(uint8array[i]);
  52. if (character == "\n") {
  53. break;
  54. }
  55. line += character;
  56. }
  57. return line;
  58. }
  59. /**
  60. * Reads header information from an RGBE texture stored in a native array.
  61. * More information on this format are available here:
  62. * https://en.wikipedia.org/wiki/RGBE_image_format
  63. *
  64. * @param uint8array The binary file stored in native array.
  65. * @return The header information.
  66. */
  67. public static RGBE_ReadHeader(uint8array: Uint8Array): HDRInfo {
  68. var height: number = 0;
  69. var width: number = 0;
  70. var line = this.readStringLine(uint8array, 0);
  71. if (line[0] != '#' || line[1] != '?') {
  72. throw "Bad HDR Format.";
  73. }
  74. var endOfHeader = false;
  75. var findFormat = false;
  76. var lineIndex: number = 0;
  77. do {
  78. lineIndex += (line.length + 1);
  79. line = this.readStringLine(uint8array, lineIndex);
  80. if (line == "FORMAT=32-bit_rle_rgbe") {
  81. findFormat = true;
  82. }
  83. else if (line.length == 0) {
  84. endOfHeader = true;
  85. }
  86. } while (!endOfHeader);
  87. if (!findFormat) {
  88. throw "HDR Bad header format, unsupported FORMAT";
  89. }
  90. lineIndex += (line.length + 1);
  91. line = this.readStringLine(uint8array, lineIndex);
  92. var sizeRegexp = /^\-Y (.*) \+X (.*)$/g;
  93. var match = sizeRegexp.exec(line);
  94. // TODO. Support +Y and -X if needed.
  95. if (!match || match.length < 3) {
  96. throw "HDR Bad header format, no size";
  97. }
  98. width = parseInt(match[2]);
  99. height = parseInt(match[1]);
  100. if (width < 8 || width > 0x7fff) {
  101. throw "HDR Bad header format, unsupported size";
  102. }
  103. lineIndex += (line.length + 1);
  104. return {
  105. height: height,
  106. width: width,
  107. dataPosition: lineIndex
  108. };
  109. }
  110. /**
  111. * Returns the cubemap information (each faces texture data) extracted from an RGBE texture.
  112. * This RGBE texture needs to store the information as a panorama.
  113. *
  114. * More information on this format are available here:
  115. * https://en.wikipedia.org/wiki/RGBE_image_format
  116. *
  117. * @param buffer The binary file stored in an array buffer.
  118. * @param size The expected size of the extracted cubemap.
  119. * @return The Cube Map information.
  120. */
  121. public static GetCubeMapTextureData(buffer: ArrayBuffer, size: number): CubeMapInfo {
  122. var uint8array = new Uint8Array(buffer);
  123. var hdrInfo = this.RGBE_ReadHeader(uint8array);
  124. var data = this.RGBE_ReadPixels_RLE(uint8array, hdrInfo);
  125. var cubeMapData = PanoramaToCubeMapTools.ConvertPanoramaToCubemap(data, hdrInfo.width, hdrInfo.height, size);
  126. return cubeMapData;
  127. }
  128. /**
  129. * Returns the pixels data extracted from an RGBE texture.
  130. * This pixels will be stored left to right up to down in the R G B order in one array.
  131. *
  132. * More information on this format are available here:
  133. * https://en.wikipedia.org/wiki/RGBE_image_format
  134. *
  135. * @param uint8array The binary file stored in an array buffer.
  136. * @param hdrInfo The header information of the file.
  137. * @return The pixels data in RGB right to left up to down order.
  138. */
  139. public static RGBE_ReadPixels(uint8array: Uint8Array, hdrInfo: HDRInfo): Float32Array {
  140. // Keep for multi format supports.
  141. return this.RGBE_ReadPixels_RLE(uint8array, hdrInfo);
  142. }
  143. private static RGBE_ReadPixels_RLE(uint8array: Uint8Array, hdrInfo: HDRInfo): Float32Array {
  144. var num_scanlines = hdrInfo.height;
  145. var scanline_width = hdrInfo.width;
  146. var a: number, b: number, c: number, d: number, count: number;
  147. var dataIndex = hdrInfo.dataPosition;
  148. var index = 0, endIndex = 0, i = 0;
  149. var scanLineArrayBuffer = new ArrayBuffer(scanline_width * 4); // four channel R G B E
  150. var scanLineArray = new Uint8Array(scanLineArrayBuffer);
  151. // 3 channels of 4 bytes per pixel in float.
  152. var resultBuffer = new ArrayBuffer(hdrInfo.width * hdrInfo.height * 4 * 3);
  153. var resultArray = new Float32Array(resultBuffer);
  154. // read in each successive scanline
  155. while (num_scanlines > 0) {
  156. a = uint8array[dataIndex++];
  157. b = uint8array[dataIndex++];
  158. c = uint8array[dataIndex++];
  159. d = uint8array[dataIndex++];
  160. if (a != 2 || b != 2 || (c & 0x80)) {
  161. // this file is not run length encoded
  162. throw "HDR Bad header format, not RLE";
  163. }
  164. if (((c << 8) | d) != scanline_width) {
  165. throw "HDR Bad header format, wrong scan line width";
  166. }
  167. index = 0;
  168. // read each of the four channels for the scanline into the buffer
  169. for (i = 0; i < 4; i++) {
  170. endIndex = (i + 1) * scanline_width;
  171. while (index < endIndex) {
  172. a = uint8array[dataIndex++];
  173. b = uint8array[dataIndex++];
  174. if (a > 128) {
  175. // a run of the same value
  176. count = a - 128;
  177. if ((count == 0) || (count > endIndex - index)) {
  178. throw "HDR Bad Format, bad scanline data (run)";
  179. }
  180. while (count-- > 0) {
  181. scanLineArray[index++] = b;
  182. }
  183. }
  184. else {
  185. // a non-run
  186. count = a;
  187. if ((count == 0) || (count > endIndex - index)) {
  188. throw "HDR Bad Format, bad scanline data (non-run)";
  189. }
  190. scanLineArray[index++] = b;
  191. if (--count > 0) {
  192. for (var j = 0; j < count; j++) {
  193. scanLineArray[index++] = uint8array[dataIndex++];
  194. }
  195. }
  196. }
  197. }
  198. }
  199. // now convert data from buffer into floats
  200. for (i = 0; i < scanline_width; i++) {
  201. a = scanLineArray[i];
  202. b = scanLineArray[i + scanline_width];
  203. c = scanLineArray[i + 2 * scanline_width];
  204. d = scanLineArray[i + 3 * scanline_width];
  205. this.Rgbe2float(resultArray,
  206. a, b, c, d,
  207. (hdrInfo.height - num_scanlines) * scanline_width * 3 + i * 3);
  208. }
  209. num_scanlines--;
  210. }
  211. return resultArray;
  212. }
  213. }
  214. }