in src/engine/loaders/LoaderKtx.js [100:342]
readFromBuffer(volDst, arrBuf, callbackProgress, callbackComplete) {
// prepare KTX header
const bufBytes = new Uint8Array(arrBuf);
let bufOff = 0;
if (callbackProgress !== undefined) {
callbackProgress(0.0);
}
if (bufBytes.length === 0) {
if (callbackComplete !== undefined) {
callbackComplete(LoadResult.BAD_HEADER);
}
return LoadResult.BAD_HEADER;
}
// console.log(`readFromKtx. len = ${bufBytes.length}`);
// console.log(`readFromKtx. data = ${bufBytes}`);
// read header
const arrayHeaderSign = [0xab, 0x4b, 0x54, 0x58, 0x20, 0x31, 0x31, 0xbb, 0x0d, 0x0a, 0x1a, 0x0a];
const lenHeaderSign = arrayHeaderSign.length;
let isHeaderSignCorrect = true;
let i;
// for (i = 0; i < lenHeaderSign; i++) {
// console.log(`${bufBytes[i]}`);
// }
for (i = 0; i < lenHeaderSign; i++) {
if (bufBytes[bufOff] !== arrayHeaderSign[i]) {
isHeaderSignCorrect = false;
break;
}
this.m_header.m_id += String.fromCharCode(bufBytes[bufOff]);
bufOff += 1;
}
if (!isHeaderSignCorrect) {
console.log('KTX HEADER IS WRONG');
if (callbackComplete !== undefined) {
callbackComplete(LoadResult.BAD_HEADER);
}
return LoadResult.BAD_HEADER;
}
const SIZE_DWORD = 4;
const ENDIANNESS_16 = 16;
const ENDIAN_CONST = 0x04030201;
// read endianess
this.m_header.m_endianness = LoaderKtx.readInt(bufBytes, bufOff);
bufOff += SIZE_DWORD;
if (this.m_header.m_endianness !== ENDIAN_CONST) {
const strFoundEndns = this.m_header.m_endianness.toString(ENDIANNESS_16);
// eslint-disable-next-line
console.log(`ENDIANNESS IS WRONG. Found = ${strFoundEndns} , but should be = ${ENDIAN_CONST.toString(16)}`);
if (callbackComplete !== undefined) {
callbackComplete(LoadResult.UNSUPPORTED_ENDIANNESS);
}
return LoadResult.UNSUPPORTED_ENDIANNESS;
}
// read
this.m_header.m_glType = LoaderKtx.readInt(bufBytes, bufOff);
bufOff += SIZE_DWORD;
this.m_header.m_glTypeSize = LoaderKtx.readInt(bufBytes, bufOff);
bufOff += SIZE_DWORD;
this.m_header.m_glFormat = LoaderKtx.readInt(bufBytes, bufOff);
bufOff += SIZE_DWORD;
if (
this.m_header.m_glFormat !== KtxHeader.KTX_GL_RED &&
this.m_header.m_glFormat !== KtxHeader.KTX_GL_RGB &&
this.m_header.m_glFormat !== KtxHeader.KTX_GL_RGBA
) {
console.log('KTX header.m_glFormat is WRONG');
if (callbackComplete !== undefined) {
callbackComplete(LoadResult.UNSUPPORTED_COLOR_FORMAT);
}
return LoadResult.UNSUPPORTED_COLOR_FORMAT;
}
this.m_header.m_glInternalFormat = LoaderKtx.readInt(bufBytes, bufOff);
bufOff += SIZE_DWORD;
this.m_header.m_glBaseInternalFormat = LoaderKtx.readInt(bufBytes, bufOff);
bufOff += SIZE_DWORD;
this.m_header.m_pixelWidth = LoaderKtx.readInt(bufBytes, bufOff);
bufOff += SIZE_DWORD;
this.m_header.m_pixelHeight = LoaderKtx.readInt(bufBytes, bufOff);
bufOff += SIZE_DWORD;
this.m_header.m_pixelDepth = LoaderKtx.readInt(bufBytes, bufOff);
bufOff += SIZE_DWORD;
// save to result volume
volDst.m_xDim = this.m_header.m_pixelWidth;
volDst.m_yDim = this.m_header.m_pixelHeight;
volDst.m_zDim = this.m_header.m_pixelDepth;
// check dim
const head = this.m_header;
// console.log(`check dim: ${head.m_pixelWidth} * ${head.m_pixelHeight} * ${head.m_pixelDepth}`);
const MIN_DIM = 4;
const MAX_DIM = 1024 * 8;
if (head.m_pixelWidth < MIN_DIM || head.m_pixelHeight < MIN_DIM || head.m_pixelDepth < MIN_DIM) {
console.log(`KTX dims too small: ${head.m_pixelWidth} * ${head.m_pixelHeight} * ${head.m_pixelDepth}`);
if (callbackComplete !== undefined) {
callbackComplete(LoadResult.WRONG_IMAGE_DIM_X);
}
return LoadResult.WRONG_IMAGE_DIM_X;
}
if (head.m_pixelWidth > MAX_DIM || head.m_pixelHeight > MAX_DIM || head.m_pixelDepth > MAX_DIM) {
console.log(`KTX dims too large: ${head.m_pixelWidth} * ${head.m_pixelHeight} * ${head.m_pixelDepth}`);
if (callbackComplete !== undefined) {
callbackComplete(LoadResult.WRONG_IMAGE_DIM_X);
}
return LoadResult.WRONG_IMAGE_DIM_X;
}
this.m_header.m_numberOfArrayElements = LoaderKtx.readInt(bufBytes, bufOff);
bufOff += SIZE_DWORD;
this.m_header.m_numberOfFaces = LoaderKtx.readInt(bufBytes, bufOff);
bufOff += SIZE_DWORD;
this.m_header.m_numberOfMipmapLevels = LoaderKtx.readInt(bufBytes, bufOff);
bufOff += SIZE_DWORD;
this.m_header.m_bytesOfKeyValueData = LoaderKtx.readInt(bufBytes, bufOff);
bufOff += SIZE_DWORD;
let bytesPerVoxel = 0;
const SIZE_BYTE = 1;
const SIZE_COLOR3 = 3;
const SIZE_COLOR4 = 4;
if (this.m_header.m_glFormat === KtxHeader.KTX_GL_RED) {
bytesPerVoxel = SIZE_BYTE;
} else if (this.m_header.m_glFormat === KtxHeader.KTX_GL_RGB) {
bytesPerVoxel = SIZE_COLOR3;
} else if (this.m_header.m_glFormat === KtxHeader.KTX_GL_RGBA) {
bytesPerVoxel = SIZE_COLOR4;
}
// read user data
if (this.m_header.m_bytesOfKeyValueData > 0) {
let udataOff = bufOff;
bufOff += this.m_header.m_bytesOfKeyValueData;
let xMin, yMin, zMin, xMax, yMax, zMax;
while (udataOff < bufOff) {
// read pair len
// const pairLen = KtxLoader.readInt(bufBytes, udataOff);
udataOff += SIZE_DWORD;
// read string until 0
let str = '';
let b;
for (b = bufBytes[udataOff]; b !== 0; udataOff++) {
b = bufBytes[udataOff];
if (b !== 0) {
str = str.concat(String.fromCharCode(b));
}
} // for
console.log(`UDataString = ${str}`);
// read vector
if (str === 'fBoxMin') {
xMin = LoaderKtx.readFloat(bufBytes, udataOff);
udataOff += SIZE_DWORD;
yMin = LoaderKtx.readFloat(bufBytes, udataOff);
udataOff += SIZE_DWORD;
zMin = LoaderKtx.readFloat(bufBytes, udataOff);
udataOff += SIZE_DWORD;
console.log(`vBoxMix = ${xMin} * ${yMin} * ${zMin}`);
} else if (str === 'fBoxMax') {
xMax = LoaderKtx.readFloat(bufBytes, udataOff);
udataOff += SIZE_DWORD;
yMax = LoaderKtx.readFloat(bufBytes, udataOff);
udataOff += SIZE_DWORD;
zMax = LoaderKtx.readFloat(bufBytes, udataOff);
udataOff += SIZE_DWORD;
this.m_boxSize.x = xMax - xMin;
this.m_boxSize.y = yMax - yMin;
this.m_boxSize.z = zMax - zMin;
console.log(`vBox = ${this.m_boxSize.x} * ${this.m_boxSize.y} * ${this.m_boxSize.z}`);
break;
} // if fbox max
} // while udata not ended
} // if have key data
// read image data size
this.m_dataSize = LoaderKtx.readInt(bufBytes, bufOff);
bufOff += SIZE_DWORD;
const dataSizeCalculated = this.m_header.m_pixelWidth * this.m_header.m_pixelHeight * this.m_header.m_pixelDepth * bytesPerVoxel;
if (this.m_dataSize !== dataSizeCalculated) {
console.log('!!! not implemented yet');
if (callbackComplete !== undefined) {
callbackComplete(LoadResult.WRONG_HEADER_DATA_SIZE);
}
return LoadResult.WRONG_HEADER_DATA_SIZE;
}
this.m_dataArray = new Uint8Array(this.m_dataSize);
// get power of 2 for data size
let pwr2;
let pwrFinish = false;
const MAX_POWER = 29;
for (pwr2 = MAX_POWER; pwr2 >= 0 && !pwrFinish; pwr2--) {
const val = 1 << pwr2;
if (val < this.m_dataSize) {
pwrFinish = true;
}
}
pwr2++;
// build mask for progress update
const SOME_POWER_MIN = 3;
pwr2 -= SOME_POWER_MIN;
if (pwr2 <= 0) {
pwr2 = 1;
}
const progressMask = (1 << pwr2) - 1;
for (let i = 0; i < this.m_dataSize; i++) {
this.m_dataArray[i] = bufBytes[bufOff];
bufOff += 1;
// progress update
if (callbackProgress !== undefined && (i & progressMask) === 0 && i > 0) {
const ratio = i / this.m_dataSize;
callbackProgress(ratio);
}
}
// update box, if not read
if (this.m_boxSize.x === 0.0) {
// Some artificial size: just proportional to pixels dimension
const MM_PER_PIXEL = 0.3;
this.m_boxSize.x = MM_PER_PIXEL * this.m_header.m_pixelWidth;
this.m_boxSize.x = MM_PER_PIXEL * this.m_header.m_pixelWidth;
this.m_boxSize.y = MM_PER_PIXEL * this.m_header.m_pixelHeight;
this.m_boxSize.z = MM_PER_PIXEL * this.m_header.m_pixelDepth;
console.log(`vBox = ${this.m_boxSize.x} * ${this.m_boxSize.y} * ${this.m_boxSize.z}`);
}
volDst.m_bytesPerVoxel = bytesPerVoxel;
volDst.m_dataArray = this.m_dataArray;
volDst.m_dataSize = this.m_dataSize;
volDst.m_boxSize = this.m_boxSize;
console.log(`KTX Loaded successfully with dim = ${volDst.m_xDim}*${volDst.m_yDim}*${volDst.m_zDim}. bpp=${volDst.m_bytesPerVoxel}`);
this.m_isLoadedSuccessfull = true;
if (callbackProgress !== undefined) {
callbackProgress(1.0);
}
if (callbackComplete !== undefined) {
callbackComplete(LoadResult.SUCCESS);
}
return LoadResult.SUCCESS;
} // end readFromBuffer