playcanvas
Version:
Open-source WebGL/WebGPU 3D engine for the web
140 lines (139 loc) • 3.82 kB
JavaScript
import { ReadStream } from "../../../core/read-stream.js";
import {
TEXHINT_ASSET,
ADDRESS_REPEAT,
ADDRESS_CLAMP_TO_EDGE,
FILTER_NEAREST,
PIXELFORMAT_RGBA8,
TEXTURETYPE_RGBE
} from "../../../platform/graphics/constants.js";
import { Texture } from "../../../platform/graphics/texture.js";
import { Asset } from "../../asset/asset.js";
import { TextureParser } from "./texture.js";
class HdrParser extends TextureParser {
constructor(registry) {
super();
this.maxRetries = 0;
}
load(url, callback, asset) {
Asset.fetchArrayBuffer(url.load, callback, asset, this.maxRetries);
if (asset.data && !asset.data.type) {
asset.data.type = TEXTURETYPE_RGBE;
}
}
open(url, data, device, textureOptions = {}) {
const textureData = this.parse(data);
if (!textureData) {
return null;
}
const texture = new Texture(device, {
name: url,
addressU: ADDRESS_REPEAT,
addressV: ADDRESS_CLAMP_TO_EDGE,
minFilter: FILTER_NEAREST,
magFilter: FILTER_NEAREST,
width: textureData.width,
height: textureData.height,
levels: textureData.levels,
format: PIXELFORMAT_RGBA8,
type: TEXTURETYPE_RGBE,
// RGBE can't be filtered, so mipmaps are out of the question! (unless we generated them ourselves)
mipmaps: false,
...textureOptions
});
texture.upload();
return texture;
}
// https://floyd.lbl.gov/radiance/refer/filefmts.pdf with help from http://www.graphics.cornell.edu/~bjw/rgbe/rgbe.c
parse(data) {
const readStream = new ReadStream(data);
const magic = readStream.readLine();
if (!magic.startsWith("#?RADIANCE")) {
return null;
}
const variables = {};
while (true) {
const line = readStream.readLine();
if (line.length === 0) {
break;
} else {
const parts = line.split("=");
if (parts.length === 2) {
variables[parts[0]] = parts[1];
}
}
}
if (!variables.hasOwnProperty("FORMAT")) {
return null;
}
const resolution = readStream.readLine().split(" ");
if (resolution.length !== 4) {
return null;
}
const height = parseInt(resolution[1], 10);
const width = parseInt(resolution[3], 10);
const pixels = this._readPixels(readStream, width, height, resolution[0] === "-Y");
if (!pixels) {
return null;
}
return {
width,
height,
levels: [pixels]
};
}
_readPixels(readStream, width, height, flipY) {
if (width < 8 || width > 32767) {
return this._readPixelsFlat(readStream, width, height);
}
const rgbe = [0, 0, 0, 0];
readStream.readArray(rgbe);
if (rgbe[0] !== 2 || rgbe[1] !== 2 || (rgbe[2] & 128) !== 0) {
readStream.skip(-4);
return this._readPixelsFlat(readStream, width, height);
}
const buffer = new ArrayBuffer(width * height * 4);
const view = new Uint8Array(buffer);
let scanstart = flipY ? 0 : width * 4 * (height - 1);
let x, y, i, channel, count, value;
for (y = 0; y < height; ++y) {
if (y) {
readStream.readArray(rgbe);
}
if ((rgbe[2] << 8) + rgbe[3] !== width) {
return null;
}
for (channel = 0; channel < 4; ++channel) {
x = 0;
while (x < width) {
count = readStream.readU8();
if (count > 128) {
count -= 128;
if (x + count > width) {
return null;
}
value = readStream.readU8();
for (i = 0; i < count; ++i) {
view[scanstart + channel + 4 * x++] = value;
}
} else {
if (count === 0 || x + count > width) {
return null;
}
for (i = 0; i < count; ++i) {
view[scanstart + channel + 4 * x++] = readStream.readU8();
}
}
}
}
scanstart += width * 4 * (flipY ? 1 : -1);
}
return view;
}
_readPixelsFlat(readStream, width, height) {
return readStream.remainingBytes === width * height * 4 ? new Uint8Array(readStream.arraybuffer, readStream.offset) : null;
}
}
export {
HdrParser
};