playcanvas
Version:
Open-source WebGL/WebGPU 3D engine for the web
102 lines (101 loc) • 3.69 kB
JavaScript
import { ShaderUtils } from "../../scene/shader-lib/shader-utils.js";
import { Texture } from "../../platform/graphics/texture.js";
import { BlendState } from "../../platform/graphics/blend-state.js";
import { drawQuadWithShader } from "../../scene/graphics/quad-render-utils.js";
import { RenderTarget } from "../../platform/graphics/render-target.js";
import {
FILTER_LINEAR,
ADDRESS_CLAMP_TO_EDGE,
isCompressedPixelFormat,
PIXELFORMAT_RGBA8,
SEMANTIC_POSITION
} from "../../platform/graphics/constants.js";
class CoreExporter {
// eslint-disable-next-line no-useless-constructor
constructor() {
}
textureToCanvas(texture, options = {}) {
const image = texture.getSource();
if (typeof HTMLImageElement !== "undefined" && image instanceof HTMLImageElement || typeof HTMLCanvasElement !== "undefined" && image instanceof HTMLCanvasElement || typeof OffscreenCanvas !== "undefined" && image instanceof OffscreenCanvas || typeof ImageBitmap !== "undefined" && image instanceof ImageBitmap) {
const { width: width2, height: height2 } = this.calcTextureSize(image.width, image.height, options.maxTextureSize);
const canvas = document.createElement("canvas");
canvas.width = width2;
canvas.height = height2;
const context = canvas.getContext("2d");
if (context === null) {
return Promise.resolve(void 0);
}
context.drawImage(image, 0, 0, canvas.width, canvas.height);
if (options.color) {
const { r, g, b } = options.color;
const imagedata = context.getImageData(0, 0, width2, height2);
const data = imagedata.data;
for (let i = 0; i < data.length; i += 4) {
data[i + 0] = data[i + 0] * r;
data[i + 1] = data[i + 1] * g;
data[i + 2] = data[i + 2] * b;
}
context.putImageData(imagedata, 0, 0);
}
return Promise.resolve(canvas);
}
const device = texture.device;
const { width, height } = this.calcTextureSize(texture.width, texture.height, options.maxTextureSize);
const format = isCompressedPixelFormat(texture.format) ? PIXELFORMAT_RGBA8 : texture.format;
const dstTexture = new Texture(device, {
name: "ExtractedTexture",
width,
height,
format,
cubemap: false,
mipmaps: false,
minFilter: FILTER_LINEAR,
magFilter: FILTER_LINEAR,
addressU: ADDRESS_CLAMP_TO_EDGE,
addressV: ADDRESS_CLAMP_TO_EDGE
});
const renderTarget = new RenderTarget({
colorBuffer: dstTexture,
depth: false
});
const shader = ShaderUtils.createShader(device, {
uniqueName: "ShaderCoreExporterBlit",
attributes: { vertex_position: SEMANTIC_POSITION },
vertexChunk: "fullscreenQuadVS",
fragmentChunk: "outputTex2DPS"
});
device.scope.resolve("source").setValue(texture);
device.setBlendState(BlendState.NOBLEND);
drawQuadWithShader(device, renderTarget, shader);
return dstTexture.read(0, 0, width, height, {
renderTarget,
immediate: true
}).then((textureData) => {
dstTexture.destroy();
renderTarget.destroy();
const pixels = new Uint8ClampedArray(width * height * 4);
pixels.set(textureData);
const newImage = new ImageData(pixels, width, height);
const canvas = document.createElement("canvas");
canvas.width = width;
canvas.height = height;
const newContext = canvas.getContext("2d");
if (!newContext) {
return Promise.resolve(void 0);
}
newContext.putImageData(newImage, 0, 0);
return Promise.resolve(canvas);
});
}
calcTextureSize(width, height, maxTextureSize) {
if (maxTextureSize) {
const scale = Math.min(maxTextureSize / Math.max(width, height), 1);
width = Math.round(width * scale);
height = Math.round(height * scale);
}
return { width, height };
}
}
export {
CoreExporter
};