nanogl-gltf
Version:
175 lines (174 loc) • 6 kB
JavaScript
import base64 from 'base64-js';
import UTF8 from '../lib/utf8-decoder';
import GltfIO from ".";
import { AbortSignal } from '@azure/abort-controller';
import { cancellablePromise, createNativeSignal } from '../lib/cancellation';
/**
* we need a createImageBitmap implementation which support options
* make test with small data uri png
*/
async function checkCreateImageBitmapCapability() {
if (typeof window === "undefined" || window.createImageBitmap === undefined) {
return false;
}
const uri = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVQYV2NgYAAAAAMAAWgmWQ0AAAAASUVORK5CYII=";
const request = await fetch(uri);
const blob = await request.blob();
try {
//@ts-ignore
await window.createImageBitmap(blob, {
premultiplyAlpha: 'none',
colorSpaceConversion: 'none'
});
}
catch (e) {
return false;
}
return true;
}
const CreateImageBitmapAvailable = checkCreateImageBitmapCapability();
function baseDir(p) {
const sep = p.lastIndexOf("/");
return [
p.substr(0, sep),
p.substr(sep + 1),
];
}
/**
* Loader implementation for web usage
*/
export class WebImpl {
/**
* Check if a URI starts with 'data:', meaning it's a base-64 encoded data
* @param uri URI to check
*/
isDataURI(uri) {
return (uri.indexOf('data:') === 0);
}
/**
* Decode a data URI and return the decoded data as an ArrayBuffer, will throw if not a data URI
* @param uri URI to decode
*/
decodeDataURI(uri) {
if (uri.indexOf('data:') !== 0) {
throw new Error('invalid dataURI');
}
const b64 = uri.substr(uri.indexOf(',') + 1);
return base64.toByteArray(b64).buffer;
}
/**
* Get the base directory and filename of a file path
* @param path File path to resolve
* @returns [baseDir, filename]
*/
resolveBaseDir(path) {
return baseDir(path);
}
/**
* Get the absolute path of a file path relative to a base directory
* @param path File path to resolve
* @param baseurl Base directory to resolve from
*/
resolvePath(path, baseurl) {
if (baseurl === undefined || this.isDataURI(path))
return path;
return baseurl + '/' + path;
}
/**
* Decode an ArrayBuffer encoded as UTF-8
* @param buffer ArrayBuffer to decode
* @param offset Offset in the ArrayBuffer to start decoding from
* @param length Length of the ArrayBuffer to decode
*/
decodeUTF8(buffer, offset = 0, length = undefined) {
if (length === undefined)
length = buffer.byteLength - offset;
return UTF8(new Uint8Array(buffer, offset, length));
}
/**
* Load a resource and return it as a string
* @param path Path to resource
* @param abortSignal Abort signal if you want to be able to cancel the request at any time
*/
async loadResource(path, abortSignal = AbortSignal.none) {
const signal = createNativeSignal(abortSignal);
const response = await fetch(path, { signal });
return response.text();
}
/**
* Load a resource and return it as an ArrayBuffer
* @param path Path to resource
* @param abortSignal Abort signal if you want to be able to cancel the request at any time
*/
async loadBinaryResource(path, abortSignal = AbortSignal.none) {
if (this.isDataURI(path)) {
return this.decodeDataURI(path);
}
const signal = createNativeSignal(abortSignal);
const response = await fetch(path, { signal });
return response.arrayBuffer();
}
/**
* Load an image from a Uint8Array
* @param arrayView Uint8Array containing the image data
* @param mimetype Image mimetype (image/png, image/webp, ...)
* @param abortSignal Abort signal if you want to be able to cancel the request at any time
*/
async loadImageBuffer(arrayView, mimetype, abortSignal = AbortSignal.none) {
const blob = new Blob([arrayView], { type: mimetype });
return this.loadImageBlob(blob, abortSignal);
}
/**
* Load an image from a URI
* @param uri Image URI
* @param abortSignal Abort signal if you want to be able to cancel the request at any time
*/
async loadImage(uri, abortSignal = AbortSignal.none) {
const signal = createNativeSignal(abortSignal);
const request = await fetch(uri, { signal });
const blob = await request.blob();
return this.loadImageBlob(blob, abortSignal);
}
/**
* Load a Blob as an image ready to be used as a texture
* @param blob Blob to load
* @param abortSignal Abort signal if you want to be able to cancel the request at any time
*/
async loadImageBlob(blob, abortSignal) {
let promise;
const hasCIB = await CreateImageBitmapAvailable;
if (hasCIB) {
//@ts-ignore
promise = createImageBitmap(blob, {
premultiplyAlpha: 'none',
colorSpaceConversion: 'none'
});
}
else {
const img = new window.Image();
const src = URL.createObjectURL(blob);
const loadPromise = new Promise((resolve, reject) => {
img.onload = resolve;
img.onerror = reject;
img.src = src;
}).finally(() => URL.revokeObjectURL(src));
promise = loadPromise.then(() => img);
}
return cancellablePromise(promise, abortSignal);
}
/**
* @hidden
*/
writeResource(path, data) {
throw new Error("Method not implemented.");
}
/**
* @hidden
*/
writeBinaryResource(path, data) {
throw new Error("Method not implemented.");
}
}
export const IO = new WebImpl();
const _instance = new GltfIO(IO);
export default _instance;