screenshotTools.ts 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270
  1. import { Nullable } from "../types";
  2. import { Camera } from "../Cameras/camera";
  3. import { Texture } from "../Materials/Textures/texture";
  4. import { RenderTargetTexture } from "../Materials/Textures/renderTargetTexture";
  5. import { FxaaPostProcess } from "../PostProcesses/fxaaPostProcess";
  6. import { Constants } from "../Engines/constants";
  7. import { Logger } from "./logger";
  8. import { _TypeStore } from "./typeStore";
  9. import { Tools } from "./tools";
  10. import { IScreenshotSize } from './interfaces/screenshotSize';
  11. declare type Engine = import("../Engines/engine").Engine;
  12. /**
  13. * Class containing a set of static utilities functions for screenshots
  14. */
  15. export class ScreenshotTools {
  16. /**
  17. * Captures a screenshot of the current rendering
  18. * @see http://doc.babylonjs.com/how_to/render_scene_on_a_png
  19. * @param engine defines the rendering engine
  20. * @param camera defines the source camera
  21. * @param size This parameter can be set to a single number or to an object with the
  22. * following (optional) properties: precision, width, height. If a single number is passed,
  23. * it will be used for both width and height. If an object is passed, the screenshot size
  24. * will be derived from the parameters. The precision property is a multiplier allowing
  25. * rendering at a higher or lower resolution
  26. * @param successCallback defines the callback receives a single parameter which contains the
  27. * screenshot as a string of base64-encoded characters. This string can be assigned to the
  28. * src parameter of an <img> to display it
  29. * @param mimeType defines the MIME type of the screenshot image (default: image/png).
  30. * Check your browser for supported MIME types
  31. */
  32. public static CreateScreenshot(engine: Engine, camera: Camera, size: IScreenshotSize | number, successCallback?: (data: string) => void, mimeType: string = "image/png"): void {
  33. let width = 0;
  34. let height = 0;
  35. //If a size value defined as object
  36. if (typeof(size) === 'object') {
  37. // If a precision value is specified
  38. if (size.precision) {
  39. width = Math.round(engine.getRenderWidth() * size.precision);
  40. height = Math.round(width / engine.getAspectRatio(camera));
  41. }
  42. else if (size.width && size.height) {
  43. width = size.width;
  44. height = size.height;
  45. }
  46. //If passing only width, computing height to keep display canvas ratio.
  47. else if (size.width && !size.height) {
  48. width = size.width;
  49. height = Math.round(width / engine.getAspectRatio(camera));
  50. }
  51. //If passing only height, computing width to keep display canvas ratio.
  52. else if (size.height && !size.width) {
  53. height = size.height;
  54. width = Math.round(height * engine.getAspectRatio(camera));
  55. }
  56. else {
  57. width = Math.round(engine.getRenderWidth());
  58. height = Math.round(width / engine.getAspectRatio(camera));
  59. }
  60. }
  61. //Assuming here that "size" parameter is a number
  62. else if (!isNaN(size)) {
  63. height = size;
  64. width = size;
  65. }
  66. else {
  67. Logger.Error("Invalid 'size' parameter !");
  68. return;
  69. }
  70. if (!Tools._ScreenshotCanvas) {
  71. Tools._ScreenshotCanvas = document.createElement('canvas');
  72. }
  73. Tools._ScreenshotCanvas.width = width;
  74. Tools._ScreenshotCanvas.height = height;
  75. var renderContext = Tools._ScreenshotCanvas.getContext("2d");
  76. var ratio = engine.getRenderWidth() / engine.getRenderHeight();
  77. var newWidth = width;
  78. var newHeight = newWidth / ratio;
  79. if (newHeight > height) {
  80. newHeight = height;
  81. newWidth = newHeight * ratio;
  82. }
  83. var offsetX = Math.max(0, width - newWidth) / 2;
  84. var offsetY = Math.max(0, height - newHeight) / 2;
  85. var renderingCanvas = engine.getRenderingCanvas();
  86. if (renderContext && renderingCanvas) {
  87. renderContext.drawImage(renderingCanvas, offsetX, offsetY, newWidth, newHeight);
  88. }
  89. Tools.EncodeScreenshotCanvasData(successCallback, mimeType);
  90. }
  91. /**
  92. * Captures a screenshot of the current rendering
  93. * @see http://doc.babylonjs.com/how_to/render_scene_on_a_png
  94. * @param engine defines the rendering engine
  95. * @param camera defines the source camera
  96. * @param size This parameter can be set to a single number or to an object with the
  97. * following (optional) properties: precision, width, height. If a single number is passed,
  98. * it will be used for both width and height. If an object is passed, the screenshot size
  99. * will be derived from the parameters. The precision property is a multiplier allowing
  100. * rendering at a higher or lower resolution
  101. * @param mimeType defines the MIME type of the screenshot image (default: image/png).
  102. * Check your browser for supported MIME types
  103. * @returns screenshot as a string of base64-encoded characters. This string can be assigned
  104. * to the src parameter of an <img> to display it
  105. */
  106. public static CreateScreenshotAsync(engine: Engine, camera: Camera, size: any, mimeType: string = "image/png"): Promise<string> {
  107. return new Promise((resolve, reject) => {
  108. ScreenshotTools.CreateScreenshot(engine, camera, size, (data) => {
  109. if (typeof(data) !== "undefined") {
  110. resolve(data);
  111. } else {
  112. reject(new Error("Data is undefined"));
  113. }
  114. }, mimeType);
  115. });
  116. }
  117. /**
  118. * Generates an image screenshot from the specified camera.
  119. * @see http://doc.babylonjs.com/how_to/render_scene_on_a_png
  120. * @param engine The engine to use for rendering
  121. * @param camera The camera to use for rendering
  122. * @param size This parameter can be set to a single number or to an object with the
  123. * following (optional) properties: precision, width, height. If a single number is passed,
  124. * it will be used for both width and height. If an object is passed, the screenshot size
  125. * will be derived from the parameters. The precision property is a multiplier allowing
  126. * rendering at a higher or lower resolution
  127. * @param successCallback The callback receives a single parameter which contains the
  128. * screenshot as a string of base64-encoded characters. This string can be assigned to the
  129. * src parameter of an <img> to display it
  130. * @param mimeType The MIME type of the screenshot image (default: image/png).
  131. * Check your browser for supported MIME types
  132. * @param samples Texture samples (default: 1)
  133. * @param antialiasing Whether antialiasing should be turned on or not (default: false)
  134. * @param fileName A name for for the downloaded file.
  135. */
  136. public static CreateScreenshotUsingRenderTarget(engine: Engine, camera: Camera, size: IScreenshotSize | number, successCallback?: (data: string) => void, mimeType: string = "image/png", samples: number = 1, antialiasing: boolean = false, fileName?: string): void {
  137. let width = 0;
  138. let height = 0;
  139. let targetTextureSize: number | { width: number, height: number } = 0;
  140. //If a size value defined as object
  141. if (typeof(size) === 'object') {
  142. //If a precision value is specified
  143. if (size.precision) {
  144. width = Math.round(engine.getRenderWidth() * size.precision);
  145. height = Math.round(width / engine.getAspectRatio(camera));
  146. size = { width: width, height: height };
  147. }
  148. else if (size.width && size.height) {
  149. width = size.width;
  150. height = size.height;
  151. }
  152. //If passing only width, computing height to keep display canvas ratio.
  153. else if (size.width && !size.height) {
  154. width = size.width;
  155. height = Math.round(width / engine.getAspectRatio(camera));
  156. size = { width: width, height: height };
  157. }
  158. //If passing only height, computing width to keep display canvas ratio.
  159. else if (size.height && !size.width) {
  160. height = size.height;
  161. width = Math.round(height * engine.getAspectRatio(camera));
  162. size = { width: width, height: height };
  163. }
  164. else {
  165. width = Math.round(engine.getRenderWidth());
  166. height = Math.round(width / engine.getAspectRatio(camera));
  167. }
  168. targetTextureSize = {width, height};
  169. }
  170. //Assuming here that "size" parameter is a number
  171. else if (!isNaN(size)) {
  172. height = size;
  173. width = size;
  174. targetTextureSize = size;
  175. }
  176. else {
  177. Logger.Error("Invalid 'size' parameter !");
  178. return;
  179. }
  180. var scene = camera.getScene();
  181. var previousCamera: Nullable<Camera> = null;
  182. if (scene.activeCamera !== camera) {
  183. previousCamera = scene.activeCamera;
  184. scene.activeCamera = camera;
  185. }
  186. var renderCanvas = engine.getRenderingCanvas();
  187. if (!renderCanvas) {
  188. Logger.Error("No rendering canvas found !");
  189. return;
  190. }
  191. var originalSize = { width: renderCanvas.width, height: renderCanvas.height };
  192. engine.setSize(width, height);
  193. scene.render();
  194. // At this point size can be a number, or an object (according to engine.prototype.createRenderTargetTexture method)
  195. var texture = new RenderTargetTexture("screenShot", targetTextureSize, scene, false, false, Constants.TEXTURETYPE_UNSIGNED_INT, false, Texture.NEAREST_SAMPLINGMODE);
  196. texture.renderList = null;
  197. texture.samples = samples;
  198. if (antialiasing) {
  199. texture.addPostProcess(new FxaaPostProcess('antialiasing', 1.0, scene.activeCamera));
  200. }
  201. texture.onAfterRenderObservable.add(() => {
  202. Tools.DumpFramebuffer(width, height, engine, successCallback, mimeType, fileName);
  203. });
  204. scene.incrementRenderId();
  205. scene.resetCachedMaterial();
  206. texture.render(true);
  207. texture.dispose();
  208. if (previousCamera) {
  209. scene.activeCamera = previousCamera;
  210. }
  211. engine.setSize(originalSize.width, originalSize.height);
  212. camera.getProjectionMatrix(true); // Force cache refresh;
  213. }
  214. /**
  215. * Generates an image screenshot from the specified camera.
  216. * @see http://doc.babylonjs.com/how_to/render_scene_on_a_png
  217. * @param engine The engine to use for rendering
  218. * @param camera The camera to use for rendering
  219. * @param size This parameter can be set to a single number or to an object with the
  220. * following (optional) properties: precision, width, height. If a single number is passed,
  221. * it will be used for both width and height. If an object is passed, the screenshot size
  222. * will be derived from the parameters. The precision property is a multiplier allowing
  223. * rendering at a higher or lower resolution
  224. * @param mimeType The MIME type of the screenshot image (default: image/png).
  225. * Check your browser for supported MIME types
  226. * @param samples Texture samples (default: 1)
  227. * @param antialiasing Whether antialiasing should be turned on or not (default: false)
  228. * @param fileName A name for for the downloaded file.
  229. * @returns screenshot as a string of base64-encoded characters. This string can be assigned
  230. * to the src parameter of an <img> to display it
  231. */
  232. public static CreateScreenshotUsingRenderTargetAsync(engine: Engine, camera: Camera, size: any, mimeType: string = "image/png", samples: number = 1, antialiasing: boolean = false, fileName?: string): Promise<string> {
  233. return new Promise((resolve, reject) => {
  234. ScreenshotTools.CreateScreenshotUsingRenderTarget(engine, camera, size, (data) => {
  235. if (typeof(data) !== "undefined") {
  236. resolve(data);
  237. } else {
  238. reject(new Error("Data is undefined"));
  239. }
  240. }, mimeType, samples, antialiasing, fileName);
  241. });
  242. }
  243. }
  244. Tools.CreateScreenshot = ScreenshotTools.CreateScreenshot;
  245. Tools.CreateScreenshotAsync = ScreenshotTools.CreateScreenshotAsync;
  246. Tools.CreateScreenshotUsingRenderTarget = ScreenshotTools.CreateScreenshotUsingRenderTarget;
  247. Tools.CreateScreenshotUsingRenderTargetAsync = ScreenshotTools.CreateScreenshotUsingRenderTargetAsync;