UNPKG

@babylonjs/core

Version:

Getting started? Play directly with the Babylon.js API using our [playground](https://playground.babylonjs.com/). It also contains a lot of samples to learn how to use it.

206 lines (205 loc) 9.59 kB
import { EffectRenderer, EffectWrapper } from "../Materials/effectRenderer.js"; import { Tools } from "./tools.js"; import { Clamp } from "../Maths/math.scalar.functions.js"; import { EngineStore } from "../Engines/engineStore.js"; let _dumpToolsEngine; let _enginePromise = null; async function _CreateDumpRenderer() { if (!_enginePromise) { _enginePromise = new Promise((resolve, reject) => { let canvas; let engine = null; const options = { preserveDrawingBuffer: true, depth: false, stencil: false, alpha: true, premultipliedAlpha: false, antialias: false, failIfMajorPerformanceCaveat: false, }; import("../Engines/thinEngine.js") .then(({ ThinEngine: thinEngineClass }) => { const engineInstanceCount = EngineStore.Instances.length; try { canvas = new OffscreenCanvas(100, 100); // will be resized later engine = new thinEngineClass(canvas, false, options); } catch (e) { if (engineInstanceCount < EngineStore.Instances.length) { // The engine was created by another instance, let's use it EngineStore.Instances.pop()?.dispose(); } // The browser either does not support OffscreenCanvas or WebGL context in OffscreenCanvas, fallback on a regular canvas canvas = document.createElement("canvas"); engine = new thinEngineClass(canvas, false, options); } // remove this engine from the list of instances to avoid using it for other purposes EngineStore.Instances.pop(); // However, make sure to dispose it when no other engines are left EngineStore.OnEnginesDisposedObservable.add((e) => { // guaranteed to run when no other instances are left // only dispose if it's not the current engine if (engine && e !== engine && !engine.isDisposed && EngineStore.Instances.length === 0) { // Dump the engine and the associated resources Dispose(); } }); engine.getCaps().parallelShaderCompile = undefined; const renderer = new EffectRenderer(engine); import("../Shaders/pass.fragment.js").then(({ passPixelShader }) => { if (!engine) { reject("Engine is not defined"); return; } const wrapper = new EffectWrapper({ engine, name: passPixelShader.name, fragmentShader: passPixelShader.shader, samplerNames: ["textureSampler"], }); _dumpToolsEngine = { canvas, engine, renderer, wrapper, }; resolve(_dumpToolsEngine); }); }) .catch(reject); }); } return await _enginePromise; } /** * Dumps the current bound framebuffer * @param width defines the rendering width * @param height defines the rendering height * @param engine defines the hosting engine * @param successCallback defines the callback triggered once the data are available * @param mimeType defines the mime type of the result * @param fileName defines the filename to download. If present, the result will automatically be downloaded * @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. * @returns a void promise */ export async function DumpFramebuffer(width, height, engine, successCallback, mimeType = "image/png", fileName, quality) { // Read the contents of the framebuffer const bufferView = await engine.readPixels(0, 0, width, height); const data = new Uint8Array(bufferView.buffer); DumpData(width, height, data, successCallback, mimeType, fileName, true, undefined, quality); } /** * Dumps an array buffer * @param width defines the rendering width * @param height defines the rendering height * @param data the data array * @param mimeType defines the mime type of the result * @param fileName defines the filename to download. If present, the result will automatically be downloaded * @param invertY true to invert the picture in the Y dimension * @param toArrayBuffer true to convert the data to an ArrayBuffer (encoded as `mimeType`) instead of a base64 string * @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. * @returns a promise that resolve to the final data */ export function DumpDataAsync(width, height, data, mimeType = "image/png", fileName, invertY = false, toArrayBuffer = false, quality) { return new Promise((resolve) => { DumpData(width, height, data, (result) => resolve(result), mimeType, fileName, invertY, toArrayBuffer, quality); }); } /** * Dumps an array buffer * @param width defines the rendering width * @param height defines the rendering height * @param data the data array * @param successCallback defines the callback triggered once the data are available * @param mimeType defines the mime type of the result * @param fileName defines the filename to download. If present, the result will automatically be downloaded * @param invertY true to invert the picture in the Y dimension * @param toArrayBuffer true to convert the data to an ArrayBuffer (encoded as `mimeType`) instead of a base64 string * @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. */ export function DumpData(width, height, data, successCallback, mimeType = "image/png", fileName, invertY = false, toArrayBuffer = false, quality) { _CreateDumpRenderer().then((renderer) => { renderer.engine.setSize(width, height, true); // Convert if data are float32 if (data instanceof Float32Array) { const data2 = new Uint8Array(data.length); let n = data.length; while (n--) { const v = data[n]; data2[n] = Math.round(Clamp(v) * 255); } data = data2; } // Create the image const texture = renderer.engine.createRawTexture(data, width, height, 5, false, !invertY, 1); renderer.renderer.setViewport(); renderer.renderer.applyEffectWrapper(renderer.wrapper); renderer.wrapper.effect._bindTexture("textureSampler", texture); renderer.renderer.draw(); if (toArrayBuffer) { Tools.ToBlob(renderer.canvas, (blob) => { const fileReader = new FileReader(); fileReader.onload = (event) => { const arrayBuffer = event.target.result; if (successCallback) { successCallback(arrayBuffer); } }; fileReader.readAsArrayBuffer(blob); }, mimeType, quality); } else { Tools.EncodeScreenshotCanvasData(renderer.canvas, successCallback, mimeType, fileName, quality); } texture.dispose(); }); } /** * Dispose the dump tools associated resources */ export function Dispose() { if (_dumpToolsEngine) { _dumpToolsEngine.wrapper.dispose(); _dumpToolsEngine.renderer.dispose(); _dumpToolsEngine.engine.dispose(); } else { // in cases where the engine is not yet created, we need to wait for it to dispose it _enginePromise?.then((dumpToolsEngine) => { dumpToolsEngine.wrapper.dispose(); dumpToolsEngine.renderer.dispose(); dumpToolsEngine.engine.dispose(); }); } _enginePromise = null; _dumpToolsEngine = null; } /** * Object containing a set of static utilities functions to dump data from a canvas * @deprecated use functions */ export const DumpTools = { // eslint-disable-next-line @typescript-eslint/naming-convention DumpData, // eslint-disable-next-line @typescript-eslint/naming-convention DumpDataAsync, // eslint-disable-next-line @typescript-eslint/naming-convention DumpFramebuffer, // eslint-disable-next-line @typescript-eslint/naming-convention Dispose, }; /** * This will be executed automatically for UMD and es5. * If esm dev wants the side effects to execute they will have to run it manually * Once we build native modules those need to be exported. * @internal */ const initSideEffects = () => { // References the dependencies. Tools.DumpData = DumpData; Tools.DumpDataAsync = DumpDataAsync; Tools.DumpFramebuffer = DumpFramebuffer; }; initSideEffects(); //# sourceMappingURL=dumpTools.js.map