WGLShader.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469
  1. /* *
  2. *
  3. * Copyright (c) 2019-2020 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;',
  184. 'if (hasColor) {',
  185. 'col = vColor;',
  186. '}',
  187. 'if (isCircle) {',
  188. 'tcol = texture2D(uSampler, gl_PointCoord.st);',
  189. 'col *= tcol;',
  190. 'if (tcol.r < 0.0) {',
  191. 'discard;',
  192. '} else {',
  193. 'gl_FragColor = col;',
  194. '}',
  195. '} else {',
  196. 'gl_FragColor = col;',
  197. '}',
  198. '}'
  199. /* eslint-enable max-len, @typescript-eslint/indent */
  200. ].join('\n'), uLocations = {},
  201. // The shader program
  202. shaderProgram,
  203. // Uniform handle to the perspective matrix
  204. pUniform,
  205. // Uniform for point size
  206. psUniform,
  207. // Uniform for fill color
  208. fillColorUniform,
  209. // Uniform for isBubble
  210. isBubbleUniform,
  211. // Uniform for bubble abs sizing
  212. bubbleSizeAbsUniform, bubbleSizeAreaUniform,
  213. // Skip translation uniform
  214. skipTranslationUniform,
  215. // Set to 1 if circle
  216. isCircleUniform,
  217. // Uniform for invertion
  218. isInverted,
  219. // Error stack
  220. errors = [],
  221. // Texture uniform
  222. uSamplerUniform;
  223. /**
  224. * Handle errors accumulated in errors stack
  225. * @private
  226. */
  227. function handleErrors() {
  228. if (errors.length) {
  229. error('[highcharts boost] shader error - ' + errors.join('\n'));
  230. }
  231. }
  232. /**
  233. * String to shader program
  234. * @private
  235. * @param {string} str - the program source
  236. * @param {string} type - the program type: either `vertex` or `fragment`
  237. * @returns {bool|shader}
  238. */
  239. function stringToProgram(str, type) {
  240. var t = type === 'vertex' ? gl.VERTEX_SHADER : gl.FRAGMENT_SHADER, shader = gl.createShader(t);
  241. gl.shaderSource(shader, str);
  242. gl.compileShader(shader);
  243. if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
  244. errors.push('when compiling ' +
  245. type +
  246. ' shader:\n' +
  247. gl.getShaderInfoLog(shader));
  248. return false;
  249. }
  250. return shader;
  251. }
  252. /**
  253. * Create the shader.
  254. * Loads the shader program statically defined above
  255. * @private
  256. */
  257. function createShader() {
  258. var v = stringToProgram(vertShade, 'vertex'), f = stringToProgram(fragShade, 'fragment');
  259. if (!v || !f) {
  260. shaderProgram = false;
  261. handleErrors();
  262. return false;
  263. }
  264. /**
  265. * @private
  266. */
  267. function uloc(n) {
  268. return gl.getUniformLocation(shaderProgram, n);
  269. }
  270. shaderProgram = gl.createProgram();
  271. gl.attachShader(shaderProgram, v);
  272. gl.attachShader(shaderProgram, f);
  273. gl.linkProgram(shaderProgram);
  274. if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
  275. errors.push(gl.getProgramInfoLog(shaderProgram));
  276. handleErrors();
  277. shaderProgram = false;
  278. return false;
  279. }
  280. gl.useProgram(shaderProgram);
  281. gl.bindAttribLocation(shaderProgram, 0, 'aVertexPosition');
  282. pUniform = uloc('uPMatrix');
  283. psUniform = uloc('pSize');
  284. fillColorUniform = uloc('fillColor');
  285. isBubbleUniform = uloc('isBubble');
  286. bubbleSizeAbsUniform = uloc('bubbleSizeAbs');
  287. bubbleSizeAreaUniform = uloc('bubbleSizeByArea');
  288. uSamplerUniform = uloc('uSampler');
  289. skipTranslationUniform = uloc('skipTranslation');
  290. isCircleUniform = uloc('isCircle');
  291. isInverted = uloc('isInverted');
  292. return true;
  293. }
  294. /**
  295. * Destroy the shader
  296. * @private
  297. */
  298. function destroy() {
  299. if (gl && shaderProgram) {
  300. gl.deleteProgram(shaderProgram);
  301. shaderProgram = false;
  302. }
  303. }
  304. /**
  305. * Bind the shader.
  306. * This makes the shader the active one until another one is bound,
  307. * or until 0 is bound.
  308. * @private
  309. */
  310. function bind() {
  311. if (gl && shaderProgram) {
  312. gl.useProgram(shaderProgram);
  313. }
  314. }
  315. /**
  316. * Set a uniform value.
  317. * This uses a hash map to cache uniform locations.
  318. * @private
  319. * @param name {string} - the name of the uniform to set
  320. * @param val {float} - the value to set
  321. */
  322. function setUniform(name, val) {
  323. if (gl && shaderProgram) {
  324. var u = uLocations[name] = (uLocations[name] ||
  325. gl.getUniformLocation(shaderProgram, name));
  326. gl.uniform1f(u, val);
  327. }
  328. }
  329. /**
  330. * Set the active texture
  331. * @private
  332. * @param texture - the texture
  333. */
  334. function setTexture(texture) {
  335. if (gl && shaderProgram) {
  336. gl.uniform1i(uSamplerUniform, texture);
  337. }
  338. }
  339. /**
  340. * Set if inversion state
  341. * @private
  342. * @flag is the state
  343. */
  344. function setInverted(flag) {
  345. if (gl && shaderProgram) {
  346. gl.uniform1i(isInverted, flag);
  347. }
  348. }
  349. /**
  350. * Enable/disable circle drawing
  351. * @private
  352. */
  353. function setDrawAsCircle(flag) {
  354. if (gl && shaderProgram) {
  355. gl.uniform1i(isCircleUniform, flag ? 1 : 0);
  356. }
  357. }
  358. /**
  359. * Flush
  360. * @private
  361. */
  362. function reset() {
  363. if (gl && shaderProgram) {
  364. gl.uniform1i(isBubbleUniform, 0);
  365. gl.uniform1i(isCircleUniform, 0);
  366. }
  367. }
  368. /**
  369. * Set bubble uniforms
  370. * @private
  371. * @param series {Highcharts.Series} - the series to use
  372. */
  373. function setBubbleUniforms(series, zCalcMin, zCalcMax) {
  374. var seriesOptions = series.options, zMin = Number.MAX_VALUE, zMax = -Number.MAX_VALUE;
  375. if (gl && shaderProgram && series.type === 'bubble') {
  376. zMin = pick(seriesOptions.zMin, clamp(zCalcMin, seriesOptions.displayNegative === false ?
  377. seriesOptions.zThreshold : -Number.MAX_VALUE, zMin));
  378. zMax = pick(seriesOptions.zMax, Math.max(zMax, zCalcMax));
  379. gl.uniform1i(isBubbleUniform, 1);
  380. gl.uniform1i(isCircleUniform, 1);
  381. gl.uniform1i(bubbleSizeAreaUniform, (series.options.sizeBy !== 'width'));
  382. gl.uniform1i(bubbleSizeAbsUniform, series.options
  383. .sizeByAbsoluteValue);
  384. setUniform('bubbleZMin', zMin);
  385. setUniform('bubbleZMax', zMax);
  386. setUniform('bubbleZThreshold', series.options.zThreshold);
  387. setUniform('bubbleMinSize', series.minPxSize);
  388. setUniform('bubbleMaxSize', series.maxPxSize);
  389. }
  390. }
  391. /**
  392. * Set the Color uniform.
  393. * @private
  394. * @param color {Array<float>} - an array with RGBA values
  395. */
  396. function setColor(color) {
  397. if (gl && shaderProgram) {
  398. gl.uniform4f(fillColorUniform, color[0] / 255.0, color[1] / 255.0, color[2] / 255.0, color[3]);
  399. }
  400. }
  401. /**
  402. * Set skip translation
  403. * @private
  404. */
  405. function setSkipTranslation(flag) {
  406. if (gl && shaderProgram) {
  407. gl.uniform1i(skipTranslationUniform, flag === true ? 1 : 0);
  408. }
  409. }
  410. /**
  411. * Set the perspective matrix
  412. * @private
  413. * @param m {Matrix4x4} - the matrix
  414. */
  415. function setPMatrix(m) {
  416. if (gl && shaderProgram) {
  417. gl.uniformMatrix4fv(pUniform, false, m);
  418. }
  419. }
  420. /**
  421. * Set the point size.
  422. * @private
  423. * @param p {float} - point size
  424. */
  425. function setPointSize(p) {
  426. if (gl && shaderProgram) {
  427. gl.uniform1f(psUniform, p);
  428. }
  429. }
  430. /**
  431. * Get the shader program handle
  432. * @private
  433. * @return {GLInt} - the handle for the program
  434. */
  435. function getProgram() {
  436. return shaderProgram;
  437. }
  438. if (gl) {
  439. if (!createShader()) {
  440. return false;
  441. }
  442. }
  443. return {
  444. psUniform: function () {
  445. return psUniform;
  446. },
  447. pUniform: function () {
  448. return pUniform;
  449. },
  450. fillColorUniform: function () {
  451. return fillColorUniform;
  452. },
  453. setBubbleUniforms: setBubbleUniforms,
  454. bind: bind,
  455. program: getProgram,
  456. create: createShader,
  457. setUniform: setUniform,
  458. setPMatrix: setPMatrix,
  459. setColor: setColor,
  460. setPointSize: setPointSize,
  461. setSkipTranslation: setSkipTranslation,
  462. setTexture: setTexture,
  463. setDrawAsCircle: setDrawAsCircle,
  464. reset: reset,
  465. setInverted: setInverted,
  466. destroy: destroy
  467. };
  468. }
  469. export default GLShader;