WGLShader.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468
  1. /* *
  2. *
  3. * Copyright (c) 2019-2021 Highsoft AS
  4. *
  5. * Boost module: stripped-down renderer for higher performance
  6. *
  7. * License: highcharts.com/license
  8. *
  9. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  10. *
  11. * */
  12. 'use strict';
  13. import U from '../../Core/Utilities.js';
  14. var clamp = U.clamp, error = U.error, pick = U.pick;
  15. /* eslint-disable valid-jsdoc */
  16. /**
  17. * A static shader mimicing axis translation functions found in Core/Axis
  18. *
  19. * @private
  20. * @function GLShader
  21. *
  22. * @param {WebGLContext} gl
  23. * the context in which the shader is active
  24. *
  25. * @return {*}
  26. */
  27. function GLShader(gl) {
  28. var vertShade = [
  29. /* eslint-disable max-len, @typescript-eslint/indent */
  30. '#version 100',
  31. '#define LN10 2.302585092994046',
  32. 'precision highp float;',
  33. 'attribute vec4 aVertexPosition;',
  34. 'attribute vec4 aColor;',
  35. 'varying highp vec2 position;',
  36. 'varying highp vec4 vColor;',
  37. 'uniform mat4 uPMatrix;',
  38. 'uniform float pSize;',
  39. 'uniform float translatedThreshold;',
  40. 'uniform bool hasThreshold;',
  41. 'uniform bool skipTranslation;',
  42. 'uniform float xAxisTrans;',
  43. 'uniform float xAxisMin;',
  44. 'uniform float xAxisMinPad;',
  45. 'uniform float xAxisPointRange;',
  46. 'uniform float xAxisLen;',
  47. 'uniform bool xAxisPostTranslate;',
  48. 'uniform float xAxisOrdinalSlope;',
  49. 'uniform float xAxisOrdinalOffset;',
  50. 'uniform float xAxisPos;',
  51. 'uniform bool xAxisCVSCoord;',
  52. 'uniform bool xAxisIsLog;',
  53. 'uniform bool xAxisReversed;',
  54. 'uniform float yAxisTrans;',
  55. 'uniform float yAxisMin;',
  56. 'uniform float yAxisMinPad;',
  57. 'uniform float yAxisPointRange;',
  58. 'uniform float yAxisLen;',
  59. 'uniform bool yAxisPostTranslate;',
  60. 'uniform float yAxisOrdinalSlope;',
  61. 'uniform float yAxisOrdinalOffset;',
  62. 'uniform float yAxisPos;',
  63. 'uniform bool yAxisCVSCoord;',
  64. 'uniform bool yAxisIsLog;',
  65. 'uniform bool yAxisReversed;',
  66. 'uniform bool isBubble;',
  67. 'uniform bool bubbleSizeByArea;',
  68. 'uniform float bubbleZMin;',
  69. 'uniform float bubbleZMax;',
  70. 'uniform float bubbleZThreshold;',
  71. 'uniform float bubbleMinSize;',
  72. 'uniform float bubbleMaxSize;',
  73. 'uniform bool bubbleSizeAbs;',
  74. 'uniform bool isInverted;',
  75. 'float bubbleRadius(){',
  76. 'float value = aVertexPosition.w;',
  77. 'float zMax = bubbleZMax;',
  78. 'float zMin = bubbleZMin;',
  79. 'float radius = 0.0;',
  80. 'float pos = 0.0;',
  81. 'float zRange = zMax - zMin;',
  82. 'if (bubbleSizeAbs){',
  83. 'value = value - bubbleZThreshold;',
  84. 'zMax = max(zMax - bubbleZThreshold, zMin - bubbleZThreshold);',
  85. 'zMin = 0.0;',
  86. '}',
  87. 'if (value < zMin){',
  88. 'radius = bubbleZMin / 2.0 - 1.0;',
  89. '} else {',
  90. 'pos = zRange > 0.0 ? (value - zMin) / zRange : 0.5;',
  91. 'if (bubbleSizeByArea && pos > 0.0){',
  92. 'pos = sqrt(pos);',
  93. '}',
  94. 'radius = ceil(bubbleMinSize + pos * (bubbleMaxSize - bubbleMinSize)) / 2.0;',
  95. '}',
  96. 'return radius * 2.0;',
  97. '}',
  98. 'float translate(float val,',
  99. 'float pointPlacement,',
  100. 'float localA,',
  101. 'float localMin,',
  102. 'float minPixelPadding,',
  103. 'float pointRange,',
  104. 'float len,',
  105. 'bool cvsCoord,',
  106. 'bool isLog,',
  107. 'bool reversed',
  108. '){',
  109. 'float sign = 1.0;',
  110. 'float cvsOffset = 0.0;',
  111. 'if (cvsCoord) {',
  112. 'sign *= -1.0;',
  113. 'cvsOffset = len;',
  114. '}',
  115. 'if (isLog) {',
  116. 'val = log(val) / LN10;',
  117. '}',
  118. 'if (reversed) {',
  119. 'sign *= -1.0;',
  120. 'cvsOffset -= sign * len;',
  121. '}',
  122. 'return sign * (val - localMin) * localA + cvsOffset + ',
  123. '(sign * minPixelPadding);',
  124. '}',
  125. 'float xToPixels(float value) {',
  126. 'if (skipTranslation){',
  127. 'return value;// + xAxisPos;',
  128. '}',
  129. 'return translate(value, 0.0, xAxisTrans, xAxisMin, xAxisMinPad, xAxisPointRange, xAxisLen, xAxisCVSCoord, xAxisIsLog, xAxisReversed);// + xAxisPos;',
  130. '}',
  131. 'float yToPixels(float value, float checkTreshold) {',
  132. 'float v;',
  133. 'if (skipTranslation){',
  134. 'v = value;// + yAxisPos;',
  135. '} else {',
  136. 'v = translate(value, 0.0, yAxisTrans, yAxisMin, yAxisMinPad, yAxisPointRange, yAxisLen, yAxisCVSCoord, yAxisIsLog, yAxisReversed);// + yAxisPos;',
  137. 'if (v > yAxisLen) {',
  138. 'v = yAxisLen;',
  139. '}',
  140. '}',
  141. 'if (checkTreshold > 0.0 && hasThreshold) {',
  142. 'v = min(v, translatedThreshold);',
  143. '}',
  144. 'return v;',
  145. '}',
  146. 'void main(void) {',
  147. 'if (isBubble){',
  148. 'gl_PointSize = bubbleRadius();',
  149. '} else {',
  150. 'gl_PointSize = pSize;',
  151. '}',
  152. // 'gl_PointSize = 10.0;',
  153. 'vColor = aColor;',
  154. 'if (skipTranslation && isInverted) {',
  155. // If we get translated values from JS, just swap them (x, y)
  156. 'gl_Position = uPMatrix * vec4(aVertexPosition.y + yAxisPos, aVertexPosition.x + xAxisPos, 0.0, 1.0);',
  157. '} else if (isInverted) {',
  158. // But when calculating pixel positions directly,
  159. // swap axes and values (x, y)
  160. 'gl_Position = uPMatrix * vec4(yToPixels(aVertexPosition.y, aVertexPosition.z) + yAxisPos, xToPixels(aVertexPosition.x) + xAxisPos, 0.0, 1.0);',
  161. '} else {',
  162. 'gl_Position = uPMatrix * vec4(xToPixels(aVertexPosition.x) + xAxisPos, yToPixels(aVertexPosition.y, aVertexPosition.z) + yAxisPos, 0.0, 1.0);',
  163. '}',
  164. // 'gl_Position = uPMatrix * vec4(aVertexPosition.x, aVertexPosition.y, 0.0, 1.0);',
  165. '}'
  166. /* eslint-enable max-len, @typescript-eslint/indent */
  167. ].join('\n'),
  168. // Fragment shader source
  169. fragShade = [
  170. /* eslint-disable max-len, @typescript-eslint/indent */
  171. 'precision highp float;',
  172. 'uniform vec4 fillColor;',
  173. 'varying highp vec2 position;',
  174. 'varying highp vec4 vColor;',
  175. 'uniform sampler2D uSampler;',
  176. 'uniform bool isCircle;',
  177. 'uniform bool hasColor;',
  178. // 'vec4 toColor(float value, vec2 point) {',
  179. // 'return vec4(0.0, 0.0, 0.0, 0.0);',
  180. // '}',
  181. 'void main(void) {',
  182. 'vec4 col = fillColor;',
  183. 'vec4 tcol = texture2D(uSampler, gl_PointCoord.st);',
  184. 'if (hasColor) {',
  185. 'col = vColor;',
  186. '}',
  187. 'if (isCircle) {',
  188. 'col *= tcol;',
  189. 'if (tcol.r < 0.0) {',
  190. 'discard;',
  191. '} else {',
  192. 'gl_FragColor = col;',
  193. '}',
  194. '} else {',
  195. 'gl_FragColor = col;',
  196. '}',
  197. '}'
  198. /* eslint-enable max-len, @typescript-eslint/indent */
  199. ].join('\n'), uLocations = {},
  200. // The shader program
  201. shaderProgram,
  202. // Uniform handle to the perspective matrix
  203. pUniform,
  204. // Uniform for point size
  205. psUniform,
  206. // Uniform for fill color
  207. fillColorUniform,
  208. // Uniform for isBubble
  209. isBubbleUniform,
  210. // Uniform for bubble abs sizing
  211. bubbleSizeAbsUniform, bubbleSizeAreaUniform,
  212. // Skip translation uniform
  213. skipTranslationUniform,
  214. // Set to 1 if circle
  215. isCircleUniform,
  216. // Uniform for invertion
  217. isInverted,
  218. // Error stack
  219. errors = [],
  220. // Texture uniform
  221. uSamplerUniform;
  222. /**
  223. * Handle errors accumulated in errors stack
  224. * @private
  225. */
  226. function handleErrors() {
  227. if (errors.length) {
  228. error('[highcharts boost] shader error - ' + errors.join('\n'));
  229. }
  230. }
  231. /**
  232. * String to shader program
  233. * @private
  234. * @param {string} str - the program source
  235. * @param {string} type - the program type: either `vertex` or `fragment`
  236. * @returns {bool|shader}
  237. */
  238. function stringToProgram(str, type) {
  239. var t = type === 'vertex' ? gl.VERTEX_SHADER : gl.FRAGMENT_SHADER, shader = gl.createShader(t);
  240. gl.shaderSource(shader, str);
  241. gl.compileShader(shader);
  242. if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
  243. errors.push('when compiling ' +
  244. type +
  245. ' shader:\n' +
  246. gl.getShaderInfoLog(shader));
  247. return false;
  248. }
  249. return shader;
  250. }
  251. /**
  252. * Create the shader.
  253. * Loads the shader program statically defined above
  254. * @private
  255. */
  256. function createShader() {
  257. var v = stringToProgram(vertShade, 'vertex'), f = stringToProgram(fragShade, 'fragment');
  258. if (!v || !f) {
  259. shaderProgram = false;
  260. handleErrors();
  261. return false;
  262. }
  263. /**
  264. * @private
  265. */
  266. function uloc(n) {
  267. return gl.getUniformLocation(shaderProgram, n);
  268. }
  269. shaderProgram = gl.createProgram();
  270. gl.attachShader(shaderProgram, v);
  271. gl.attachShader(shaderProgram, f);
  272. gl.linkProgram(shaderProgram);
  273. if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
  274. errors.push(gl.getProgramInfoLog(shaderProgram));
  275. handleErrors();
  276. shaderProgram = false;
  277. return false;
  278. }
  279. gl.useProgram(shaderProgram);
  280. gl.bindAttribLocation(shaderProgram, 0, 'aVertexPosition');
  281. pUniform = uloc('uPMatrix');
  282. psUniform = uloc('pSize');
  283. fillColorUniform = uloc('fillColor');
  284. isBubbleUniform = uloc('isBubble');
  285. bubbleSizeAbsUniform = uloc('bubbleSizeAbs');
  286. bubbleSizeAreaUniform = uloc('bubbleSizeByArea');
  287. uSamplerUniform = uloc('uSampler');
  288. skipTranslationUniform = uloc('skipTranslation');
  289. isCircleUniform = uloc('isCircle');
  290. isInverted = uloc('isInverted');
  291. return true;
  292. }
  293. /**
  294. * Destroy the shader
  295. * @private
  296. */
  297. function destroy() {
  298. if (gl && shaderProgram) {
  299. gl.deleteProgram(shaderProgram);
  300. shaderProgram = false;
  301. }
  302. }
  303. /**
  304. * Bind the shader.
  305. * This makes the shader the active one until another one is bound,
  306. * or until 0 is bound.
  307. * @private
  308. */
  309. function bind() {
  310. if (gl && shaderProgram) {
  311. gl.useProgram(shaderProgram);
  312. }
  313. }
  314. /**
  315. * Set a uniform value.
  316. * This uses a hash map to cache uniform locations.
  317. * @private
  318. * @param name {string} - the name of the uniform to set
  319. * @param val {float} - the value to set
  320. */
  321. function setUniform(name, val) {
  322. if (gl && shaderProgram) {
  323. var u = uLocations[name] = (uLocations[name] ||
  324. gl.getUniformLocation(shaderProgram, name));
  325. gl.uniform1f(u, val);
  326. }
  327. }
  328. /**
  329. * Set the active texture
  330. * @private
  331. * @param texture - the texture
  332. */
  333. function setTexture(texture) {
  334. if (gl && shaderProgram) {
  335. gl.uniform1i(uSamplerUniform, texture);
  336. }
  337. }
  338. /**
  339. * Set if inversion state
  340. * @private
  341. * @flag is the state
  342. */
  343. function setInverted(flag) {
  344. if (gl && shaderProgram) {
  345. gl.uniform1i(isInverted, flag);
  346. }
  347. }
  348. /**
  349. * Enable/disable circle drawing
  350. * @private
  351. */
  352. function setDrawAsCircle(flag) {
  353. if (gl && shaderProgram) {
  354. gl.uniform1i(isCircleUniform, flag ? 1 : 0);
  355. }
  356. }
  357. /**
  358. * Flush
  359. * @private
  360. */
  361. function reset() {
  362. if (gl && shaderProgram) {
  363. gl.uniform1i(isBubbleUniform, 0);
  364. gl.uniform1i(isCircleUniform, 0);
  365. }
  366. }
  367. /**
  368. * Set bubble uniforms
  369. * @private
  370. * @param series {Highcharts.Series} - the series to use
  371. */
  372. function setBubbleUniforms(series, zCalcMin, zCalcMax) {
  373. var seriesOptions = series.options, zMin = Number.MAX_VALUE, zMax = -Number.MAX_VALUE;
  374. if (gl && shaderProgram && series.type === 'bubble') {
  375. zMin = pick(seriesOptions.zMin, clamp(zCalcMin, seriesOptions.displayNegative === false ?
  376. seriesOptions.zThreshold : -Number.MAX_VALUE, zMin));
  377. zMax = pick(seriesOptions.zMax, Math.max(zMax, zCalcMax));
  378. gl.uniform1i(isBubbleUniform, 1);
  379. gl.uniform1i(isCircleUniform, 1);
  380. gl.uniform1i(bubbleSizeAreaUniform, (series.options.sizeBy !== 'width'));
  381. gl.uniform1i(bubbleSizeAbsUniform, series.options
  382. .sizeByAbsoluteValue);
  383. setUniform('bubbleZMin', zMin);
  384. setUniform('bubbleZMax', zMax);
  385. setUniform('bubbleZThreshold', series.options.zThreshold);
  386. setUniform('bubbleMinSize', series.minPxSize);
  387. setUniform('bubbleMaxSize', series.maxPxSize);
  388. }
  389. }
  390. /**
  391. * Set the Color uniform.
  392. * @private
  393. * @param color {Array<float>} - an array with RGBA values
  394. */
  395. function setColor(color) {
  396. if (gl && shaderProgram) {
  397. gl.uniform4f(fillColorUniform, color[0] / 255.0, color[1] / 255.0, color[2] / 255.0, color[3]);
  398. }
  399. }
  400. /**
  401. * Set skip translation
  402. * @private
  403. */
  404. function setSkipTranslation(flag) {
  405. if (gl && shaderProgram) {
  406. gl.uniform1i(skipTranslationUniform, flag === true ? 1 : 0);
  407. }
  408. }
  409. /**
  410. * Set the perspective matrix
  411. * @private
  412. * @param m {Matrix4x4} - the matrix
  413. */
  414. function setPMatrix(m) {
  415. if (gl && shaderProgram) {
  416. gl.uniformMatrix4fv(pUniform, false, m);
  417. }
  418. }
  419. /**
  420. * Set the point size.
  421. * @private
  422. * @param p {float} - point size
  423. */
  424. function setPointSize(p) {
  425. if (gl && shaderProgram) {
  426. gl.uniform1f(psUniform, p);
  427. }
  428. }
  429. /**
  430. * Get the shader program handle
  431. * @private
  432. * @return {GLInt} - the handle for the program
  433. */
  434. function getProgram() {
  435. return shaderProgram;
  436. }
  437. if (gl) {
  438. if (!createShader()) {
  439. return false;
  440. }
  441. }
  442. return {
  443. psUniform: function () {
  444. return psUniform;
  445. },
  446. pUniform: function () {
  447. return pUniform;
  448. },
  449. fillColorUniform: function () {
  450. return fillColorUniform;
  451. },
  452. setBubbleUniforms: setBubbleUniforms,
  453. bind: bind,
  454. program: getProgram,
  455. create: createShader,
  456. setUniform: setUniform,
  457. setPMatrix: setPMatrix,
  458. setColor: setColor,
  459. setPointSize: setPointSize,
  460. setSkipTranslation: setSkipTranslation,
  461. setTexture: setTexture,
  462. setDrawAsCircle: setDrawAsCircle,
  463. reset: reset,
  464. setInverted: setInverted,
  465. destroy: destroy
  466. };
  467. }
  468. export default GLShader;