dumpTools.js 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. import { ThinEngine } from "../Engines/thinEngine.js";
  2. import { EffectRenderer, EffectWrapper } from "../Materials/effectRenderer.js";
  3. import { Tools } from "./tools.js";
  4. import { passPixelShader } from "../Shaders/pass.fragment.js";
  5. import { Scalar } from "../Maths/math.scalar.js";
  6. /**
  7. * Class containing a set of static utilities functions to dump data from a canvas
  8. */
  9. export class DumpTools {
  10. static _CreateDumpRenderer() {
  11. if (!DumpTools._DumpToolsEngine) {
  12. let canvas;
  13. let engine = null;
  14. const options = {
  15. preserveDrawingBuffer: true,
  16. depth: false,
  17. stencil: false,
  18. alpha: true,
  19. premultipliedAlpha: false,
  20. antialias: false,
  21. failIfMajorPerformanceCaveat: false,
  22. };
  23. try {
  24. canvas = new OffscreenCanvas(100, 100); // will be resized later
  25. engine = new ThinEngine(canvas, false, options);
  26. }
  27. catch (e) {
  28. // The browser either does not support OffscreenCanvas or WebGL context in OffscreenCanvas, fallback on a regular canvas
  29. canvas = document.createElement("canvas");
  30. engine = new ThinEngine(canvas, false, options);
  31. }
  32. engine.getCaps().parallelShaderCompile = undefined;
  33. const renderer = new EffectRenderer(engine);
  34. const wrapper = new EffectWrapper({
  35. engine,
  36. name: passPixelShader.name,
  37. fragmentShader: passPixelShader.shader,
  38. samplerNames: ["textureSampler"],
  39. });
  40. DumpTools._DumpToolsEngine = {
  41. canvas,
  42. engine,
  43. renderer,
  44. wrapper,
  45. };
  46. }
  47. return DumpTools._DumpToolsEngine;
  48. }
  49. /**
  50. * Dumps the current bound framebuffer
  51. * @param width defines the rendering width
  52. * @param height defines the rendering height
  53. * @param engine defines the hosting engine
  54. * @param successCallback defines the callback triggered once the data are available
  55. * @param mimeType defines the mime type of the result
  56. * @param fileName defines the filename to download. If present, the result will automatically be downloaded
  57. * @param quality The quality of the image if lossy mimeType is used (e.g. image/jpeg, image/webp). See {@link https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toBlob | HTMLCanvasElement.toBlob()}'s `quality` parameter.
  58. * @returns a void promise
  59. */
  60. static async DumpFramebuffer(width, height, engine, successCallback, mimeType = "image/png", fileName, quality) {
  61. // Read the contents of the framebuffer
  62. const bufferView = await engine.readPixels(0, 0, width, height);
  63. const data = new Uint8Array(bufferView.buffer);
  64. DumpTools.DumpData(width, height, data, successCallback, mimeType, fileName, true, undefined, quality);
  65. }
  66. /**
  67. * Dumps an array buffer
  68. * @param width defines the rendering width
  69. * @param height defines the rendering height
  70. * @param data the data array
  71. * @param mimeType defines the mime type of the result
  72. * @param fileName defines the filename to download. If present, the result will automatically be downloaded
  73. * @param invertY true to invert the picture in the Y dimension
  74. * @param toArrayBuffer true to convert the data to an ArrayBuffer (encoded as `mimeType`) instead of a base64 string
  75. * @param quality The quality of the image if lossy mimeType is used (e.g. image/jpeg, image/webp). See {@link https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toBlob | HTMLCanvasElement.toBlob()}'s `quality` parameter.
  76. * @returns a promise that resolve to the final data
  77. */
  78. static DumpDataAsync(width, height, data, mimeType = "image/png", fileName, invertY = false, toArrayBuffer = false, quality) {
  79. return new Promise((resolve) => {
  80. DumpTools.DumpData(width, height, data, (result) => resolve(result), mimeType, fileName, invertY, toArrayBuffer, quality);
  81. });
  82. }
  83. /**
  84. * Dumps an array buffer
  85. * @param width defines the rendering width
  86. * @param height defines the rendering height
  87. * @param data the data array
  88. * @param successCallback defines the callback triggered once the data are available
  89. * @param mimeType defines the mime type of the result
  90. * @param fileName defines the filename to download. If present, the result will automatically be downloaded
  91. * @param invertY true to invert the picture in the Y dimension
  92. * @param toArrayBuffer true to convert the data to an ArrayBuffer (encoded as `mimeType`) instead of a base64 string
  93. * @param quality The quality of the image if lossy mimeType is used (e.g. image/jpeg, image/webp). See {@link https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toBlob | HTMLCanvasElement.toBlob()}'s `quality` parameter.
  94. */
  95. static DumpData(width, height, data, successCallback, mimeType = "image/png", fileName, invertY = false, toArrayBuffer = false, quality) {
  96. const renderer = DumpTools._CreateDumpRenderer();
  97. renderer.engine.setSize(width, height, true);
  98. // Convert if data are float32
  99. if (data instanceof Float32Array) {
  100. const data2 = new Uint8Array(data.length);
  101. let n = data.length;
  102. while (n--) {
  103. const v = data[n];
  104. data2[n] = Math.round(Scalar.Clamp(v) * 255);
  105. }
  106. data = data2;
  107. }
  108. // Create the image
  109. const texture = renderer.engine.createRawTexture(data, width, height, 5, false, !invertY, 1);
  110. renderer.renderer.setViewport();
  111. renderer.renderer.applyEffectWrapper(renderer.wrapper);
  112. renderer.wrapper.effect._bindTexture("textureSampler", texture);
  113. renderer.renderer.draw();
  114. if (toArrayBuffer) {
  115. Tools.ToBlob(renderer.canvas, (blob) => {
  116. const fileReader = new FileReader();
  117. fileReader.onload = (event) => {
  118. const arrayBuffer = event.target.result;
  119. if (successCallback) {
  120. successCallback(arrayBuffer);
  121. }
  122. };
  123. fileReader.readAsArrayBuffer(blob);
  124. }, mimeType, quality);
  125. }
  126. else {
  127. Tools.EncodeScreenshotCanvasData(renderer.canvas, successCallback, mimeType, fileName, quality);
  128. }
  129. texture.dispose();
  130. }
  131. /**
  132. * Dispose the dump tools associated resources
  133. */
  134. static Dispose() {
  135. if (DumpTools._DumpToolsEngine) {
  136. DumpTools._DumpToolsEngine.wrapper.dispose();
  137. DumpTools._DumpToolsEngine.renderer.dispose();
  138. DumpTools._DumpToolsEngine.engine.dispose();
  139. }
  140. DumpTools._DumpToolsEngine = null;
  141. }
  142. }
  143. /**
  144. * This will be executed automatically for UMD and es5.
  145. * If esm dev wants the side effects to execute they will have to run it manually
  146. * Once we build native modules those need to be exported.
  147. * @internal
  148. */
  149. const initSideEffects = () => {
  150. // References the dependencies.
  151. Tools.DumpData = DumpTools.DumpData;
  152. Tools.DumpDataAsync = DumpTools.DumpDataAsync;
  153. Tools.DumpFramebuffer = DumpTools.DumpFramebuffer;
  154. };
  155. initSideEffects();
  156. //# sourceMappingURL=dumpTools.js.map