vtf-js
Version:
A javascript IO library for the Valve Texture Format.
98 lines (97 loc) • 4.68 kB
JavaScript
import { VFormats } from '../core/enums.js';
import { VEncodedImageData, VImageData, registerCodec, HAS_FLOAT16 } from '../core/image.js';
const PixelDataTypes = {
'Uint8': 1,
'Uint16': 2,
'Uint32': 4,
'Float16': 2,
'Float32': 4,
};
const PixelArrayTypes = {
'Uint8': Uint8Array,
'Uint16': Uint16Array,
'Uint32': Uint32Array,
'Float16': HAS_FLOAT16 ? Float16Array : null,
'Float32': Float32Array,
};
const PixelFloatTypes = {
'Float16': true,
'Float32': true,
};
function createGenericRGBA(format, type, red, green, blue, alpha, avg = false, isFloat = false) {
const SET = 'set' + type;
const GET = 'get' + type;
const ARR = PixelArrayTypes[type];
const maxValue = (type in PixelFloatTypes) ? 1 : 2 ** (ARR.BYTES_PER_ELEMENT * 8) - 1;
const increment = +(red != null) + +(green != null) + +(blue != null) + +(alpha != null);
const bpp = PixelDataTypes[type] * increment;
// console.log('Creating format', VFormats[format], type, ARR, bpp);
return {
length(width, height) {
return width * height * bpp;
},
encode(source) {
const image = source.convert(ARR);
const length = image.width * image.height;
const out = new Uint8Array(length * bpp);
const view = new DataView(out.buffer);
for (let p = 0; p < length; p++) {
const s = p * 4;
const t = p * bpp;
if (red != null)
view[SET](t + red, image.data[s], true);
if (green != null)
view[SET](t + green, image.data[s + 1], true);
if (blue != null)
view[SET](t + blue, image.data[s + 2], true);
if (alpha != null)
view[SET](t + alpha, image.data[s + 3], true);
}
return new VEncodedImageData(out, image.width, image.height, format);
},
decode(source) {
const view = new DataView(source.data.buffer);
const length = source.width * source.height;
const out = new ARR(length * 4);
for (let p = 0; p < length; p++) {
const s = p * bpp;
const t = p * 4;
if (avg) {
out[t] = out[t + 1] = out[t + 2] = view[GET](s, true);
out[t + 3] = maxValue;
continue;
}
if (red != null)
out[t] = view[GET](s + red, true);
if (green != null)
out[t + 1] = view[GET](s + green, true);
if (blue != null)
out[t + 2] = view[GET](s + blue, true);
if (alpha != null)
out[t + 3] = view[GET](s + alpha, true);
else
out[t + 3] = maxValue;
}
return new VImageData(out, source.width, source.height);
},
};
}
registerCodec(VFormats.RGBA8888, createGenericRGBA(VFormats.RGBA8888, 'Uint8', 0, 1, 2, 3));
registerCodec(VFormats.BGRA8888, createGenericRGBA(VFormats.BGRA8888, 'Uint8', 2, 1, 0, 3));
registerCodec(VFormats.BGRX8888, createGenericRGBA(VFormats.BGRX8888, 'Uint8', 2, 1, 0, 3));
registerCodec(VFormats.ABGR8888, createGenericRGBA(VFormats.ABGR8888, 'Uint8', 3, 2, 1, 0));
registerCodec(VFormats.ARGB8888, createGenericRGBA(VFormats.ARGB8888, 'Uint8', 1, 2, 3, 0));
registerCodec(VFormats.RGB888, createGenericRGBA(VFormats.RGB888, 'Uint8', 0, 1, 2, null));
registerCodec(VFormats.BGR888, createGenericRGBA(VFormats.BGR888, 'Uint8', 2, 1, 0, null));
registerCodec(VFormats.UV88, createGenericRGBA(VFormats.UV88, 'Uint8', 0, 1, null, null));
registerCodec(VFormats.A8, createGenericRGBA(VFormats.A8, 'Uint8', null, null, null, 0));
registerCodec(VFormats.I8, createGenericRGBA(VFormats.I8, 'Uint8', 0, null, null, null, true));
registerCodec(VFormats.P8, createGenericRGBA(VFormats.P8, 'Uint8', 0, null, null, null, true));
registerCodec(VFormats.R32F, createGenericRGBA(VFormats.R32F, 'Float32', 0, null, null, null));
registerCodec(VFormats.RGB323232F, createGenericRGBA(VFormats.RGB323232F, 'Float32', 0, 4, 8, null));
registerCodec(VFormats.RGBA16161616, createGenericRGBA(VFormats.RGBA16161616, 'Uint16', 0, 2, 4, 6));
registerCodec(VFormats.RGBA32323232F, createGenericRGBA(VFormats.RGBA32323232F, 'Float32', 0, 4, 8, 12));
if (HAS_FLOAT16)
registerCodec(VFormats.RGBA16161616F, createGenericRGBA(VFormats.RGBA16161616F, 'Float16', 0, 2, 4, 6));
else
console.warn(`vtf-js: Your environment does not support Float16Array. RGBA16161616F codec has not been registered!`);