210 lines
5.6 KiB
Haxe
210 lines
5.6 KiB
Haxe
package kha.internal;
|
|
|
|
import haxe.io.UInt8Array;
|
|
import haxe.io.Float32Array;
|
|
import haxe.io.Bytes;
|
|
|
|
// Based on https://github.com/vorg/pragmatic-pbr/blob/master/local_modules/parse-hdr/parse-hdr.js
|
|
class HdrFormat {
|
|
static var radiancePattern = new EReg("#\\?RADIANCE", "i");
|
|
static var commentPattern = new EReg("#.*", "i");
|
|
static var gammaPattern = new EReg("GAMMA=", "i");
|
|
static var exposurePattern = new EReg("EXPOSURE=\\s*([0-9]*[.][0-9]*)", "i");
|
|
static var formatPattern = new EReg("FORMAT=32-bit_rle_rgbe", "i");
|
|
static var widthHeightPattern = new EReg("-Y ([0-9]+) \\+X ([0-9]+)", "i");
|
|
static var buffer: UInt8Array;
|
|
static var bufferLength: Int;
|
|
static var fileOffset: Int;
|
|
|
|
static function readBuf(buf: UInt8Array): Int {
|
|
var bytesRead = 0;
|
|
do {
|
|
buf[bytesRead++] = buffer[fileOffset];
|
|
} while (++fileOffset < bufferLength && bytesRead < buf.length);
|
|
return bytesRead;
|
|
}
|
|
|
|
static function readBufOffset(buf: UInt8Array, offset: Int, length: Int): Int {
|
|
var bytesRead = 0;
|
|
do {
|
|
buf[offset + bytesRead++] = buffer[fileOffset];
|
|
} while (++fileOffset < bufferLength && bytesRead < length);
|
|
return bytesRead;
|
|
}
|
|
|
|
static function readPixelsRaw(buffer: UInt8Array, data: UInt8Array, offset: Int, numpixels: Int) {
|
|
var numExpected = 4 * numpixels;
|
|
var numRead = readBufOffset(data, offset, numExpected);
|
|
if (numRead < numExpected) {
|
|
trace("Error reading raw pixels: got " + numRead + " bytes, expected " + numExpected);
|
|
return;
|
|
}
|
|
}
|
|
|
|
static function readPixelsRawRLE(buffer: UInt8Array, data: UInt8Array, offset: Int, scanline_width: Int, num_scanlines: Int) {
|
|
var rgbe = new UInt8Array(4);
|
|
var scanline_buffer: UInt8Array = null;
|
|
var ptr: Int;
|
|
var ptr_end: Int;
|
|
var count: Int;
|
|
var buf = new UInt8Array(2);
|
|
// var bufferLength = buffer.length;
|
|
|
|
while (num_scanlines > 0) {
|
|
if (readBuf(rgbe) < rgbe.length) {
|
|
trace("Error reading bytes: expected " + rgbe.length);
|
|
return;
|
|
}
|
|
|
|
if ((rgbe[0] != 2) || (rgbe[1] != 2) || ((rgbe[2] & 0x80) != 0)) {
|
|
// This file is not run length encoded
|
|
data[offset++] = rgbe[0];
|
|
data[offset++] = rgbe[1];
|
|
data[offset++] = rgbe[2];
|
|
data[offset++] = rgbe[3];
|
|
readPixelsRaw(buffer, data, offset, scanline_width * num_scanlines - 1);
|
|
return;
|
|
}
|
|
|
|
if ((((rgbe[2] & 0xFF) << 8) | (rgbe[3] & 0xFF)) != scanline_width) {
|
|
trace("Wrong scanline width " + (((rgbe[2] & 0xFF) << 8) | (rgbe[3] & 0xFF)) + ", expected " + scanline_width);
|
|
return;
|
|
}
|
|
|
|
if (scanline_buffer == null) {
|
|
scanline_buffer = new UInt8Array(4 * scanline_width);
|
|
}
|
|
|
|
ptr = 0;
|
|
// Read each of the four channels for the scanline into the buffer
|
|
for (i in 0...4) {
|
|
ptr_end = (i + 1) * scanline_width;
|
|
while (ptr < ptr_end) {
|
|
if (readBuf(buf) < buf.length) {
|
|
trace("Error reading 2-byte buffer");
|
|
return;
|
|
}
|
|
if ((buf[0] & 0xFF) > 128) {
|
|
// A run of the same value
|
|
count = (buf[0] & 0xFF) - 128;
|
|
if ((count == 0) || (count > ptr_end - ptr)) {
|
|
trace("Bad scanline data");
|
|
return;
|
|
}
|
|
while (count-- > 0) {
|
|
scanline_buffer[ptr++] = buf[1];
|
|
}
|
|
}
|
|
else {
|
|
// A non-run
|
|
count = buf[0] & 0xFF;
|
|
if ((count == 0) || (count > ptr_end - ptr)) {
|
|
trace("Bad scanline data");
|
|
return;
|
|
}
|
|
scanline_buffer[ptr++] = buf[1];
|
|
if (--count > 0) {
|
|
if (readBufOffset(scanline_buffer, ptr, count) < count) {
|
|
trace("Error reading non-run data");
|
|
return;
|
|
}
|
|
ptr += count;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Copy byte data to output
|
|
for (i in 0...scanline_width) {
|
|
data[offset + 0] = scanline_buffer[i];
|
|
data[offset + 1] = scanline_buffer[i + scanline_width];
|
|
data[offset + 2] = scanline_buffer[i + 2 * scanline_width];
|
|
data[offset + 3] = scanline_buffer[i + 3 * scanline_width];
|
|
offset += 4;
|
|
}
|
|
|
|
num_scanlines--;
|
|
}
|
|
}
|
|
|
|
static function readLine(): String {
|
|
var buf = "";
|
|
do {
|
|
var b = buffer[fileOffset];
|
|
if (b == 10) { // New line
|
|
++fileOffset;
|
|
break;
|
|
}
|
|
buf += String.fromCharCode(b);
|
|
} while (++fileOffset < bufferLength);
|
|
return buf;
|
|
}
|
|
|
|
public static function parse(bytes: Bytes) {
|
|
buffer = UInt8Array.fromBytes(bytes);
|
|
bufferLength = buffer.length;
|
|
fileOffset = 0;
|
|
var width = 0;
|
|
var height = 0;
|
|
var exposure = 1.0;
|
|
// var gamma = 1.0;
|
|
var rle = false;
|
|
|
|
for (i in 0...20) {
|
|
var line = readLine();
|
|
if (formatPattern.match(line)) {
|
|
rle = true;
|
|
}
|
|
else if (exposurePattern.match(line)) {
|
|
exposure = Std.parseFloat(exposurePattern.matched(1));
|
|
}
|
|
else if (widthHeightPattern.match(line)) {
|
|
height = Std.parseInt(widthHeightPattern.matched(1));
|
|
width = Std.parseInt(widthHeightPattern.matched(2));
|
|
break;
|
|
}
|
|
// else if (radiancePattern.match(line)) {}
|
|
// else if (commentPattern.match(line)) {}
|
|
}
|
|
|
|
if (!rle) {
|
|
trace("File is not run length encoded!");
|
|
return null;
|
|
}
|
|
|
|
var data = new UInt8Array(width * height * 4);
|
|
var scanline_width = width;
|
|
var num_scanlines = height;
|
|
|
|
readPixelsRawRLE(buffer, data, 0, scanline_width, num_scanlines);
|
|
|
|
// TODO: should be Float16
|
|
var floatData = new Float32Array(width * height * 4);
|
|
var offset = 0;
|
|
while (offset < data.length) {
|
|
var r = data[offset + 0] / 255;
|
|
var g = data[offset + 1] / 255;
|
|
var b = data[offset + 2] / 255;
|
|
var e = data[offset + 3];
|
|
var f = Math.pow(2.0, e - 128.0);
|
|
r *= f;
|
|
g *= f;
|
|
b *= f;
|
|
|
|
floatData[offset + 0] = r;
|
|
floatData[offset + 1] = g;
|
|
floatData[offset + 2] = b;
|
|
floatData[offset + 3] = 1.0;
|
|
offset += 4;
|
|
}
|
|
|
|
return {
|
|
width: width,
|
|
height: height,
|
|
// exposure: exposure,
|
|
// gamma: gamma,
|
|
data: floatData
|
|
}
|
|
}
|
|
}
|