@babylonjs/core
Version:
Getting started? Play directly with the Babylon.js API using our [playground](https://playground.babylonjs.com/). It also contains a lot of samples to learn how to use it.
284 lines (283 loc) • 12.6 kB
JavaScript
import { CompressionCodes, DecodeFloat32, ParseFloat16, ParseFloat32, ParseInt32, ParseInt64, ParseUint16, ParseUint32 } from "./exrLoader.core.js";
import { UncompressPIZ, UncompressPXR, UncompressRAW, UncompressRLE, UncompressZIP } from "./exrLoader.compression.js";
import { FLOAT32_SIZE, INT16_SIZE } from "./exrLoader.interfaces.js";
import { Tools } from "../../../../Misc/tools.js";
import { ExrLoaderGlobalConfiguration, EXROutputType } from "./exrLoader.configuration.js";
/**
* Inspired by https://github.com/sciecode/three.js/blob/dev/examples/jsm/loaders/EXRLoader.js
* Referred to the original Industrial Light & Magic OpenEXR implementation and the TinyEXR / Syoyo Fujita
* implementation.
*/
// /*
// Copyright (c) 2014 - 2017, Syoyo Fujita
// All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of the Syoyo Fujita nor the
// names of its contributors may be used to endorse or promote products
// derived from this software without specific prior written permission.
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// */
// // TinyEXR contains some OpenEXR code, which is licensed under ------------
// ///////////////////////////////////////////////////////////////////////////
// //
// // Copyright (c) 2002, Industrial Light & Magic, a division of Lucas
// // Digital Ltd. LLC
// //
// // All rights reserved.
// //
// // Redistribution and use in source and binary forms, with or without
// // modification, are permitted provided that the following conditions are
// // met:
// // * Redistributions of source code must retain the above copyright
// // notice, this list of conditions and the following disclaimer.
// // * Redistributions in binary form must reproduce the above
// // copyright notice, this list of conditions and the following disclaimer
// // in the documentation and/or other materials provided with the
// // distribution.
// // * Neither the name of Industrial Light & Magic nor the names of
// // its contributors may be used to endorse or promote products derived
// // from this software without specific prior written permission.
// //
// // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// //
// ///////////////////////////////////////////////////////////////////////////
// // End of OpenEXR license -------------------------------------------------
/**
* Create a decoder for the exr file
* @param header header of the exr file
* @param dataView dataview of the exr file
* @param offset current offset
* @param outputType expected output type (float or half float)
* @returns a promise that resolves with the decoder
*/
export async function CreateDecoderAsync(header, dataView, offset, outputType) {
const decoder = {
size: 0,
viewer: dataView,
array: new Uint8Array(dataView.buffer),
offset: offset,
width: header.dataWindow.xMax - header.dataWindow.xMin + 1,
height: header.dataWindow.yMax - header.dataWindow.yMin + 1,
channels: header.channels.length,
channelLineOffsets: {},
scanOrder: () => 0,
bytesPerLine: 0,
outLineWidth: 0,
lines: 0,
scanlineBlockSize: 0,
inputSize: null,
type: 0,
uncompress: null,
getter: () => 0,
format: 5,
outputChannels: 0,
decodeChannels: {},
blockCount: null,
byteArray: null,
linearSpace: false,
textureType: 0,
};
switch (header.compression) {
case CompressionCodes.NO_COMPRESSION:
decoder.lines = 1;
decoder.uncompress = UncompressRAW;
break;
case CompressionCodes.RLE_COMPRESSION:
decoder.lines = 1;
decoder.uncompress = UncompressRLE;
break;
case CompressionCodes.ZIPS_COMPRESSION:
decoder.lines = 1;
decoder.uncompress = UncompressZIP;
await Tools.LoadScriptAsync(ExrLoaderGlobalConfiguration.FFLATEUrl);
break;
case CompressionCodes.ZIP_COMPRESSION:
decoder.lines = 16;
decoder.uncompress = UncompressZIP;
await Tools.LoadScriptAsync(ExrLoaderGlobalConfiguration.FFLATEUrl);
break;
case CompressionCodes.PIZ_COMPRESSION:
decoder.lines = 32;
decoder.uncompress = UncompressPIZ;
break;
case CompressionCodes.PXR24_COMPRESSION:
decoder.lines = 16;
decoder.uncompress = UncompressPXR;
await Tools.LoadScriptAsync(ExrLoaderGlobalConfiguration.FFLATEUrl);
break;
default:
throw new Error(CompressionCodes[header.compression] + " is unsupported");
}
decoder.scanlineBlockSize = decoder.lines;
const channels = {};
for (const channel of header.channels) {
switch (channel.name) {
case "Y":
case "R":
case "G":
case "B":
case "A":
channels[channel.name] = true;
decoder.type = channel.pixelType;
}
}
// RGB images will be converted to RGBA format, preventing software emulation in select devices.
let fillAlpha = false;
if (channels.R && channels.G && channels.B) {
fillAlpha = !channels.A;
decoder.outputChannels = 4;
decoder.decodeChannels = { R: 0, G: 1, B: 2, A: 3 };
}
else if (channels.Y) {
decoder.outputChannels = 1;
decoder.decodeChannels = { Y: 0 };
}
else {
throw new Error("EXRLoader.parse: file contains unsupported data channels.");
}
if (decoder.type === 1) {
// half
switch (outputType) {
case EXROutputType.Float:
decoder.getter = ParseFloat16;
decoder.inputSize = INT16_SIZE;
break;
case EXROutputType.HalfFloat:
decoder.getter = ParseUint16;
decoder.inputSize = INT16_SIZE;
break;
}
}
else if (decoder.type === 2) {
// float
switch (outputType) {
case EXROutputType.Float:
decoder.getter = ParseFloat32;
decoder.inputSize = FLOAT32_SIZE;
break;
case EXROutputType.HalfFloat:
decoder.getter = DecodeFloat32;
decoder.inputSize = FLOAT32_SIZE;
}
}
else {
throw new Error("Unsupported pixelType " + decoder.type + " for " + header.compression);
}
decoder.blockCount = decoder.height / decoder.scanlineBlockSize;
for (let i = 0; i < decoder.blockCount; i++) {
ParseInt64(dataView, offset); // scanlineOffset
}
// we should be passed the scanline offset table, ready to start reading pixel data.
const size = decoder.width * decoder.height * decoder.outputChannels;
switch (outputType) {
case EXROutputType.Float:
decoder.byteArray = new Float32Array(size);
decoder.textureType = 1;
// Fill initially with 1s for the alpha value if the texture is not RGBA, RGB values will be overwritten
if (fillAlpha) {
decoder.byteArray.fill(1, 0, size);
}
break;
case EXROutputType.HalfFloat:
decoder.byteArray = new Uint16Array(size);
decoder.textureType = 2;
if (fillAlpha) {
decoder.byteArray.fill(0x3c00, 0, size); // Uint16Array holds half float data, 0x3C00 is 1
}
break;
default:
throw new Error("Unsupported type: " + outputType);
}
let byteOffset = 0;
for (const channel of header.channels) {
if (decoder.decodeChannels[channel.name] !== undefined) {
decoder.channelLineOffsets[channel.name] = byteOffset * decoder.width;
}
byteOffset += channel.pixelType * 2;
}
decoder.bytesPerLine = decoder.width * byteOffset;
decoder.outLineWidth = decoder.width * decoder.outputChannels;
if (header.lineOrder === "INCREASING_Y") {
decoder.scanOrder = (y) => y;
}
else {
decoder.scanOrder = (y) => decoder.height - 1 - y;
}
if (decoder.outputChannels == 4) {
decoder.format = 5;
decoder.linearSpace = true;
}
else {
decoder.format = 6;
decoder.linearSpace = false;
}
return decoder;
}
/**
* Scan the data of the exr file
* @param decoder decoder to use
* @param header header of the exr file
* @param dataView dataview of the exr file
* @param offset current offset
*/
export function ScanData(decoder, header, dataView, offset) {
const tmpOffset = { value: 0 };
for (let scanlineBlockIdx = 0; scanlineBlockIdx < decoder.height / decoder.scanlineBlockSize; scanlineBlockIdx++) {
const line = ParseInt32(dataView, offset) - header.dataWindow.yMin; // line_no
decoder.size = ParseUint32(dataView, offset); // data_len
decoder.lines = line + decoder.scanlineBlockSize > decoder.height ? decoder.height - line : decoder.scanlineBlockSize;
const isCompressed = decoder.size < decoder.lines * decoder.bytesPerLine;
const viewer = isCompressed && decoder.uncompress ? decoder.uncompress(decoder) : UncompressRAW(decoder);
offset.value += decoder.size;
for (let line_y = 0; line_y < decoder.scanlineBlockSize; line_y++) {
const scan_y = scanlineBlockIdx * decoder.scanlineBlockSize;
const true_y = line_y + decoder.scanOrder(scan_y);
if (true_y >= decoder.height) {
continue;
}
const lineOffset = line_y * decoder.bytesPerLine;
const outLineOffset = (decoder.height - 1 - true_y) * decoder.outLineWidth;
for (let channelID = 0; channelID < decoder.channels; channelID++) {
const name = header.channels[channelID].name;
const lOff = decoder.channelLineOffsets[name];
const cOff = decoder.decodeChannels[name];
if (cOff === undefined) {
continue;
}
tmpOffset.value = lineOffset + lOff;
for (let x = 0; x < decoder.width; x++) {
const outIndex = outLineOffset + x * decoder.outputChannels + cOff;
if (decoder.byteArray) {
decoder.byteArray[outIndex] = decoder.getter(viewer, tmpOffset);
}
}
}
}
}
}
//# sourceMappingURL=exrLoader.decoder.js.map