lut-reader.js 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  1. function splitOnSpaceHandleQuotesWithEscapes(str, splits = ' \t\n\r') {
  2. const strs = [];
  3. let quoteType;
  4. let escape;
  5. let s = [];
  6. for (let i = 0; i < str.length; ++i) {
  7. const c = str[i];
  8. if (escape) {
  9. escape = false;
  10. s.push(c);
  11. } else {
  12. if (quoteType) { // we're inside quotes
  13. if (c === quoteType) {
  14. quoteType = undefined;
  15. strs.push(s.join(''));
  16. s = [];
  17. } else if (c === '\\') {
  18. escape = true;
  19. } else {
  20. s.push(c);
  21. }
  22. } else { // we're not in quotes
  23. if (splits.indexOf(c) >= 0) {
  24. if (s.length) {
  25. strs.push(s.join(''));
  26. s = [];
  27. }
  28. } else if (c === '"' || c === '\'') {
  29. if (s.length) { // its in th middle of a word
  30. s.push(c);
  31. } else {
  32. quoteType = c;
  33. }
  34. } else {
  35. s.push(c);
  36. }
  37. }
  38. }
  39. }
  40. if (s.length || strs.length === 0) {
  41. strs.push(s.join(''));
  42. }
  43. return strs;
  44. }
  45. export function parse(str) {
  46. const data = [];
  47. const lut = {
  48. name: 'unknonw',
  49. type: '1D',
  50. size: 0,
  51. data,
  52. min: [0, 0, 0],
  53. max: [1, 1, 1],
  54. };
  55. const lines = str.split('\n');
  56. for (const origLine of lines) {
  57. const hashNdx = origLine.indexOf('#');
  58. const line = hashNdx >= 0 ? origLine.substring(0, hashNdx) : origLine;
  59. const parts = splitOnSpaceHandleQuotesWithEscapes(line);
  60. switch (parts[0].toUpperCase()) {
  61. case 'TITLE':
  62. lut.name = parts[1];
  63. break;
  64. case 'LUT_1D_SIZE':
  65. lut.size = parseInt(parts[1]);
  66. lut.type = '1D';
  67. break;
  68. case 'LUT_3D_SIZE':
  69. lut.size = parseInt(parts[1]);
  70. lut.type = '3D';
  71. break;
  72. case 'DOMAIN_MIN':
  73. lut.min = parts.slice(1).map(parseFloat);
  74. break;
  75. case 'DOMAIN_MAX':
  76. lut.max = parts.slice(1).map(parseFloat);
  77. break;
  78. default:
  79. if (parts.length === 3) {
  80. data.push(...parts.map(parseFloat));
  81. }
  82. break;
  83. }
  84. }
  85. if (!lut.min) {
  86. lut.min = data.slice(0, 3);
  87. lut.max = data.slice(data.length - 3, data.length);
  88. }
  89. if (!lut.size) {
  90. lut.size = data.length / 3;
  91. }
  92. return lut;
  93. }
  94. function lerp(a, b, t) {
  95. return a + (b - a) * t;
  96. }
  97. function lut1Dto3D(lut) {
  98. let src = lut.data;
  99. if (src.length / 3 !== lut.size) {
  100. src = [];
  101. for (let i = 0; i < lut.size; ++i) {
  102. const u = i / lut.size * lut.data.length;
  103. const i0 = (u | 0) * 3;
  104. const i1 = i0 + 3;
  105. const t = u % 1;
  106. src.push(
  107. lerp(lut.data[i0 + 0], lut.data[i1 + 0], t),
  108. lerp(lut.data[i0 + 0], lut.data[i1 + 1], t),
  109. lerp(lut.data[i0 + 0], lut.data[i1 + 2], t),
  110. );
  111. }
  112. }
  113. const data = [];
  114. for (let i = 0; i < lut.size * lut.size; ++i) {
  115. data.push(...src);
  116. }
  117. return Object.assign({}, lut, {data});
  118. }
  119. export function lutTo2D3Drgb8(lut) {
  120. if (lut.type === '1D') {
  121. lut = lut1Dto3D(lut);
  122. }
  123. const min = lut.min;
  124. const max = lut.max;
  125. const range = min.map((min, ndx) => {
  126. return max[ndx] - min;
  127. });
  128. const src = lut.data;
  129. const data = new Uint8Array(src.length);
  130. for (let i = 0; i < src.length; i += 3) {
  131. data[i + 0] = (src[i + 0] - min[0]) / range[0] * 255;
  132. data[i + 1] = (src[i + 1] - min[1]) / range[1] * 255;
  133. data[i + 2] = (src[i + 2] - min[2]) / range[2] * 255;
  134. }
  135. return Object.assign({}, lut, {data});
  136. }