mirror of
https://github.com/storytold/spark.git
synced 2026-10-17 00:12:59 +00:00
Use OffscreenCanvas and WebGL2 context to decode webp images (#90)
This commit is contained in:
Generated
-16
@@ -9,7 +9,6 @@
|
|||||||
"version": "0.1.4",
|
"version": "0.1.4",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@jsquash/webp": "^1.5.0",
|
|
||||||
"fflate": "^0.8.2"
|
"fflate": "^0.8.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
@@ -707,15 +706,6 @@
|
|||||||
"@jridgewell/sourcemap-codec": "^1.4.10"
|
"@jridgewell/sourcemap-codec": "^1.4.10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@jsquash/webp": {
|
|
||||||
"version": "1.5.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@jsquash/webp/-/webp-1.5.0.tgz",
|
|
||||||
"integrity": "sha512-KggLoj2MnRSfIqTeKe1EmbljTX2vuV7mh79k89PCL1pyqiDULcPM1L47twxXt0hkb68F70bXiL31MxsuoZtKFw==",
|
|
||||||
"license": "Apache-2.0",
|
|
||||||
"dependencies": {
|
|
||||||
"wasm-feature-detect": "^1.2.11"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@microsoft/api-extractor": {
|
"node_modules/@microsoft/api-extractor": {
|
||||||
"version": "7.52.8",
|
"version": "7.52.8",
|
||||||
"resolved": "https://registry.npmjs.org/@microsoft/api-extractor/-/api-extractor-7.52.8.tgz",
|
"resolved": "https://registry.npmjs.org/@microsoft/api-extractor/-/api-extractor-7.52.8.tgz",
|
||||||
@@ -2900,12 +2890,6 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/wasm-feature-detect": {
|
|
||||||
"version": "1.8.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/wasm-feature-detect/-/wasm-feature-detect-1.8.0.tgz",
|
|
||||||
"integrity": "sha512-zksaLKM2fVlnB5jQQDqKXXwYHLQUVH9es+5TOOHwGOVJOCeRBCiPjwSg+3tN2AdTCzjgli4jijCH290kXb/zWQ==",
|
|
||||||
"license": "Apache-2.0"
|
|
||||||
},
|
|
||||||
"node_modules/which": {
|
"node_modules/which": {
|
||||||
"version": "2.0.2",
|
"version": "2.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
||||||
|
|||||||
@@ -58,7 +58,6 @@
|
|||||||
"spark-internal-rs": "file:rust/spark-internal-rs/pkg"
|
"spark-internal-rs": "file:rust/spark-internal-rs/pkg"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@jsquash/webp": "^1.5.0",
|
|
||||||
"fflate": "^0.8.2"
|
"fflate": "^0.8.2"
|
||||||
},
|
},
|
||||||
"keywords": ["3d", "three.js", "gsplats", "3dgs", "gaussian", "splats"]
|
"keywords": ["3d", "three.js", "gsplats", "3dgs", "gaussian", "splats"]
|
||||||
|
|||||||
+49
-3
@@ -1,4 +1,3 @@
|
|||||||
import { decode as decodeWebp } from "@jsquash/webp";
|
|
||||||
import type { PcSogsJson } from "./SplatLoader";
|
import type { PcSogsJson } from "./SplatLoader";
|
||||||
import {
|
import {
|
||||||
computeMaxSplats,
|
computeMaxSplats,
|
||||||
@@ -149,9 +148,56 @@ export async function unpackPcSogs(
|
|||||||
return { packedArray, numSplats, extra };
|
return { packedArray, numSplats, extra };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WebGL context for reading raw pixel data of WebP images
|
||||||
|
let offscreenGlContext: WebGL2RenderingContext | null = null;
|
||||||
|
|
||||||
async function decodeImage(fileBytes: ArrayBuffer) {
|
async function decodeImage(fileBytes: ArrayBuffer) {
|
||||||
const { data: rgba, width, height } = await decodeWebp(fileBytes);
|
if (!offscreenGlContext) {
|
||||||
return { rgba, width, height };
|
const canvas = new OffscreenCanvas(1, 1);
|
||||||
|
offscreenGlContext = canvas.getContext("webgl2");
|
||||||
|
if (!offscreenGlContext) {
|
||||||
|
throw new Error("Failed to create WebGL2 context");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const imageBlob = new Blob([fileBytes]);
|
||||||
|
const bitmap = await createImageBitmap(imageBlob, {
|
||||||
|
premultiplyAlpha: "none",
|
||||||
|
});
|
||||||
|
|
||||||
|
const gl = offscreenGlContext;
|
||||||
|
const texture = gl.createTexture();
|
||||||
|
gl.bindTexture(gl.TEXTURE_2D, texture);
|
||||||
|
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
|
||||||
|
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, bitmap);
|
||||||
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
|
||||||
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
|
||||||
|
|
||||||
|
const framebuffer = gl.createFramebuffer();
|
||||||
|
gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
|
||||||
|
gl.framebufferTexture2D(
|
||||||
|
gl.FRAMEBUFFER,
|
||||||
|
gl.COLOR_ATTACHMENT0,
|
||||||
|
gl.TEXTURE_2D,
|
||||||
|
texture,
|
||||||
|
0,
|
||||||
|
);
|
||||||
|
|
||||||
|
const data = new Uint8Array(bitmap.width * bitmap.height * 4);
|
||||||
|
gl.readPixels(
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
bitmap.width,
|
||||||
|
bitmap.height,
|
||||||
|
gl.RGBA,
|
||||||
|
gl.UNSIGNED_BYTE,
|
||||||
|
data,
|
||||||
|
);
|
||||||
|
|
||||||
|
gl.deleteTexture(texture);
|
||||||
|
gl.deleteFramebuffer(framebuffer);
|
||||||
|
|
||||||
|
return { rgba: data, width: bitmap.width, height: bitmap.height };
|
||||||
}
|
}
|
||||||
|
|
||||||
async function decodeImageRgba(fileBytes: ArrayBuffer) {
|
async function decodeImageRgba(fileBytes: ArrayBuffer) {
|
||||||
|
|||||||
Reference in New Issue
Block a user