rw-parser
Version:
Parses RenderWare DFF and TXD files into usable format!
513 lines (509 loc) • 22.1 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ImageDecoder = void 0;
// Source: https://github.com/Parik27/DragonFF/blob/master/gtaLib/txd.py
var ImageDecoder = /** @class */ (function () {
function ImageDecoder() {
}
ImageDecoder.readUInt16LE = function (buf, offset) {
return buf[offset] | (buf[offset + 1] << 8);
};
ImageDecoder.readUInt32LE = function (buf, offset) {
return (buf[offset] |
(buf[offset + 1] << 8) |
(buf[offset + 2] << 16) |
(buf[offset + 3] << 24));
};
ImageDecoder.decode565 = function (bits) {
var r = (bits >> 11) & 31;
var g = (bits >> 5) & 63;
var b = bits & 31;
return [
(r << 3) | (r >> 2),
(g << 2) | (g >> 4),
(b << 3) | (b >> 2)
];
};
ImageDecoder.decode555 = function (bits) {
var r = Math.round(((bits >> 10) & 31) * 255 / 31);
var g = Math.round(((bits >> 5) & 31) * 255 / 31);
var b = Math.round((bits & 31) * 255 / 31);
return [r, g, b];
};
ImageDecoder.decode1555 = function (bits) {
var a = Math.round(((bits >> 15) & 1) * 255);
var r = Math.round(((bits >> 10) & 31) * 255 / 31);
var g = Math.round(((bits >> 5) & 31) * 255 / 31);
var b = Math.round((bits & 31) * 255 / 31);
return [a, r, g, b];
};
ImageDecoder.decode4444 = function (bits) {
var a = Math.round(((bits >> 12) & 15) * 255 / 15);
var r = Math.round(((bits >> 8) & 15) * 255 / 15);
var g = Math.round(((bits >> 4) & 15) * 255 / 15);
var b = Math.round((bits & 15) * 255 / 15);
return [a, r, g, b];
};
/*
bc1 - block compression format, using for DXT1
compress 4x4 block of pixels
format:
+---------------+
| color0 | color0 in palette. 16bit (RGB 565 format)
+---------------+
| color1 | color1 in palette. 16bit (RGB 565 format)
+---+---+---+---+
| a | b | c | d | a-p color palette index 2bit * 16
+---+---+---+---+
| e | f | g | h |
+---+---+---+---+
| i | j | k | l |
+---+---+---+---+
| m | n | o | p | total: 8byte in 4x4 colors
+---+---+---+---+
color2 and color3 in the palette are calculated by interpolating other colors or choosing the average between them.
color0 > color1 => interpolation, else => average
*/
ImageDecoder.bc1 = function (data, width, height) {
var rgba = new Uint8Array(4 * width * height);
var colorPalette = new Uint8Array(16);
var offset = 0;
for (var y = 0; y < height; y += 4) {
for (var x = 0; x < width; x += 4) {
var color0 = ImageDecoder.readUInt16LE(data, offset);
var color1 = ImageDecoder.readUInt16LE(data, offset + 2);
var colorBits = ImageDecoder.readUInt32LE(data, offset + 4);
offset += 8;
var _a = ImageDecoder.decode565(color0), c0r = _a[0], c0g = _a[1], c0b = _a[2];
var _b = ImageDecoder.decode565(color1), c1r = _b[0], c1g = _b[1], c1b = _b[2];
if (color0 > color1) {
colorPalette[0] = c0r;
colorPalette[1] = c0g;
colorPalette[2] = c0b;
colorPalette[4] = c1r;
colorPalette[5] = c1g;
colorPalette[6] = c1b;
colorPalette[8] = (2 * c0r + c1r) / 3;
colorPalette[9] = (2 * c0g + c1g) / 3;
colorPalette[10] = (2 * c0b + c1b) / 3;
colorPalette[12] = (c0r + 2 * c1r) / 3;
colorPalette[13] = (c0g + 2 * c1g) / 3;
colorPalette[14] = (c0b + 2 * c1b) / 3;
}
else {
colorPalette[0] = c0r;
colorPalette[1] = c0g;
colorPalette[2] = c0b;
colorPalette[4] = c1r;
colorPalette[5] = c1g;
colorPalette[6] = c1b;
colorPalette[8] = (c0r + c1r) >> 1;
colorPalette[9] = (c0g + c1g) >> 1;
colorPalette[10] = (c0b + c1b) >> 1;
colorPalette[12] = 0;
colorPalette[13] = 0;
colorPalette[14] = 0;
}
var baseIndex = (y * width + x) * 4;
for (var k = 0; k < 16; k++) {
var colorIdx = colorBits & 0x3;
colorBits >>>= 2;
var j = k >> 2;
var i = k & 3;
var idx = baseIndex + ((j * width + i) << 2);
rgba[idx + 0] = colorPalette[colorIdx * 4];
rgba[idx + 1] = colorPalette[colorIdx * 4 + 1];
rgba[idx + 2] = colorPalette[colorIdx * 4 + 2];
if (color0 <= color1 && colorIdx === 3) {
rgba[idx + 3] = 0;
}
else {
rgba[idx + 3] = 255;
}
}
}
}
return rgba;
};
/*
bc2 - block compression format, using for DXT2 and DXT3
compress 4x4 block of pixels with 4x4 4bit alpha
format:
+---+---+---+---+
| a | b | c | d | a-p pixel alpha. 4bit * 16
+---+---+---+---+
| e | f | g | h |
+---+---+---+---+
| i | j | k | l |
+---+---+---+---+
| m | n | o | p |
+---+---+---+---+
| | bc1 color compression. 8byte
| bc1 block |
| | total: 16byte in 4x4 colors
+---------------+
in DXT2, the color data is interpreted as being premultiplied by alpha
*/
ImageDecoder.bc2 = function (data, width, height, premultiplied) {
var rgba = new Uint8Array(4 * width * height);
var colorPalette = new Uint8Array(16);
var alphaTable = new Uint8Array(16);
for (var i = 0; i < 16; i++) {
alphaTable[i] = (i * 255 + 7.5) / 15 | 0;
}
var offset = 0;
for (var y = 0; y < height; y += 4) {
for (var x = 0; x < width; x += 4) {
var alpha0 = ImageDecoder.readUInt32LE(data, offset);
var alpha1 = ImageDecoder.readUInt32LE(data, offset + 4);
offset += 8;
var color0 = ImageDecoder.readUInt16LE(data, offset);
var color1 = ImageDecoder.readUInt16LE(data, offset + 2);
var colorBits = ImageDecoder.readUInt32LE(data, offset + 4);
offset += 8;
var _a = ImageDecoder.decode565(color0), c0r = _a[0], c0g = _a[1], c0b = _a[2];
var _b = ImageDecoder.decode565(color1), c1r = _b[0], c1g = _b[1], c1b = _b[2];
if (color0 > color1) {
colorPalette[0] = c0r;
colorPalette[1] = c0g;
colorPalette[2] = c0b;
colorPalette[4] = c1r;
colorPalette[5] = c1g;
colorPalette[6] = c1b;
colorPalette[8] = (2 * c0r + c1r) / 3;
colorPalette[9] = (2 * c0g + c1g) / 3;
colorPalette[10] = (2 * c0b + c1b) / 3;
colorPalette[12] = (c0r + 2 * c1r) / 3;
colorPalette[13] = (c0g + 2 * c1g) / 3;
colorPalette[14] = (c0b + 2 * c1b) / 3;
}
else {
colorPalette[0] = c0r;
colorPalette[1] = c0g;
colorPalette[2] = c0b;
colorPalette[4] = c1r;
colorPalette[5] = c1g;
colorPalette[6] = c1b;
colorPalette[8] = (c0r + c1r) >> 1;
colorPalette[9] = (c0g + c1g) >> 1;
colorPalette[10] = (c0b + c1b) >> 1;
colorPalette[12] = 0;
colorPalette[13] = 0;
colorPalette[14] = 0;
}
var baseIndex = ((y * width + x) << 2);
for (var k = 0; k < 16; k++) {
var j = k >> 2;
var i = k & 3;
var idx = baseIndex + ((((j * width + i) << 2)));
var colorIdx = colorBits & 0x3;
colorBits >>>= 2;
var bitPos = (j << 2) + i;
var byteIndex = bitPos >> 3;
var shift = (bitPos & 7) << 2;
var alpha4 = ((byteIndex === 0 ? alpha0 : alpha1) >>> shift) & 0xF;
var alpha = alphaTable[alpha4];
rgba[idx + 0] = colorPalette[colorIdx * 4];
rgba[idx + 1] = colorPalette[colorIdx * 4 + 1];
rgba[idx + 2] = colorPalette[colorIdx * 4 + 2];
rgba[idx + 3] = alpha;
if (premultiplied && alpha > 0 && alpha < 255) {
var factor = 255 / alpha;
rgba[idx + 0] = Math.min(255, Math.round(rgba[idx + 0] * factor));
rgba[idx + 1] = Math.min(255, Math.round(rgba[idx + 1] * factor));
rgba[idx + 2] = Math.min(255, Math.round(rgba[idx + 2] * factor));
}
}
}
}
return rgba;
};
/*
bc3 - block compression format, using for DXT4 and DXT5
compress 4x4 block of pixels with alpha
format:
+---------------+
| alpha0 | min alpha value. 8bit
+---------------+
| alpha1 | max alpha value. 8bit
+---+---+---+---+
| a | b | c | d | bc1-like alpha block but 3bit * 16 (index in alpha palette)
+---+---+---+---+
| e | f | g | h |
+---+---+---+---+
| i | j | k | l |
+---+---+---+---+
| m | n | o | p |
+---+---+---+---+
| | bc1 color compression. 8byte
| bc1 block |
| | total: 16byte in 4x4 colors
+---------------+
in DXT4, the color data is interpreted as being premultiplied by alpha
*/
ImageDecoder.bc3 = function (data, width, height, premultiplied) {
var rgba = new Uint8Array(4 * width * height);
var alphaPalette = new Uint8Array(8);
var colorPalette = new Uint8Array(16);
var alphaIndices = new Uint8Array(16);
var offset = 0;
for (var y = 0; y < height; y += 4) {
for (var x = 0; x < width; x += 4) {
var alpha0 = data[offset++];
var alpha1 = data[offset++];
var alphaBits = data.subarray(offset, offset + 6);
offset += 6;
var color0 = ImageDecoder.readUInt16LE(data, offset);
var color1 = ImageDecoder.readUInt16LE(data, offset + 2);
var colorBits = ImageDecoder.readUInt32LE(data, offset + 4);
offset += 8;
var _a = ImageDecoder.decode565(color0), c0r = _a[0], c0g = _a[1], c0b = _a[2];
var _b = ImageDecoder.decode565(color1), c1r = _b[0], c1g = _b[1], c1b = _b[2];
if (color0 > color1) {
colorPalette[0] = c0r;
colorPalette[1] = c0g;
colorPalette[2] = c0b;
colorPalette[4] = c1r;
colorPalette[5] = c1g;
colorPalette[6] = c1b;
colorPalette[8] = (2 * c0r + c1r) / 3;
colorPalette[9] = (2 * c0g + c1g) / 3;
colorPalette[10] = (2 * c0b + c1b) / 3;
colorPalette[12] = (c0r + 2 * c1r) / 3;
colorPalette[13] = (c0g + 2 * c1g) / 3;
colorPalette[14] = (c0b + 2 * c1b) / 3;
}
else {
colorPalette[0] = c0r;
colorPalette[1] = c0g;
colorPalette[2] = c0b;
colorPalette[4] = c1r;
colorPalette[5] = c1g;
colorPalette[6] = c1b;
colorPalette[8] = (c0r + c1r) >> 1;
colorPalette[9] = (c0g + c1g) >> 1;
colorPalette[10] = (c0b + c1b) >> 1;
colorPalette[12] = 0;
colorPalette[13] = 0;
colorPalette[14] = 0;
}
if (alpha0 > alpha1) {
alphaPalette[0] = alpha0;
alphaPalette[1] = alpha1;
alphaPalette[2] = (alpha0 * 6 + alpha1 * 1 + 3) / 7;
alphaPalette[3] = (alpha0 * 5 + alpha1 * 2 + 3) / 7;
alphaPalette[4] = (alpha0 * 4 + alpha1 * 3 + 3) / 7;
alphaPalette[5] = (alpha0 * 3 + alpha1 * 4 + 3) / 7;
alphaPalette[6] = (alpha0 * 2 + alpha1 * 5 + 3) / 7;
alphaPalette[7] = (alpha0 * 1 + alpha1 * 6 + 3) / 7;
}
else {
alphaPalette[0] = alpha0;
alphaPalette[1] = alpha1;
alphaPalette[2] = (alpha0 * 4 + alpha1 * 1 + 2) / 5;
alphaPalette[3] = (alpha0 * 3 + alpha1 * 2 + 2) / 5;
alphaPalette[4] = (alpha0 * 2 + alpha1 * 3 + 2) / 5;
alphaPalette[5] = (alpha0 * 1 + alpha1 * 4 + 2) / 5;
alphaPalette[6] = 0;
alphaPalette[7] = 255;
}
for (var k = 0; k < 16; k++) {
var bitOffset = k * 3;
var byteOffset = bitOffset >> 3;
var shift = bitOffset & 7;
if (shift <= 5) {
alphaIndices[k] = (alphaBits[byteOffset] >> shift) & 0x7;
}
else {
var part1 = (alphaBits[byteOffset] >> shift) & 0x7;
var part2 = (alphaBits[byteOffset + 1] << (8 - shift)) & 0x7;
alphaIndices[k] = part1 | part2;
}
}
var baseIndex = (y * width + x) << 2;
var bits = colorBits;
for (var k = 0; k < 16; k++) {
var j = k >> 2;
var i = k & 3;
var idx = baseIndex + ((((j * width + i) << 2)));
var colorIdx = bits & 0x3;
bits >>>= 2;
var alpha = alphaPalette[alphaIndices[k] & 0x7];
rgba[idx + 0] = colorPalette[colorIdx * 4];
rgba[idx + 1] = colorPalette[colorIdx * 4 + 1];
rgba[idx + 2] = colorPalette[colorIdx * 4 + 2];
rgba[idx + 3] = alpha;
if (premultiplied && alpha > 0 && alpha < 255) {
var factor = 255 / alpha;
rgba[idx] = Math.min(255, Math.round(rgba[idx] * factor));
rgba[idx + 1] = Math.min(255, Math.round(rgba[idx + 1] * factor));
rgba[idx + 2] = Math.min(255, Math.round(rgba[idx + 2] * factor));
}
}
}
}
return rgba;
};
ImageDecoder.bgra1555 = function (data, width, height) {
var rbga = new Uint8Array(4 * width * height);
var offset = 0;
for (var i = 0; i < data.length; i += 2) {
var color = ImageDecoder.readUInt16LE(data, i);
var _a = ImageDecoder.decode1555(color), a = _a[0], r = _a[1], g = _a[2], b = _a[3];
rbga[offset++] = r;
rbga[offset++] = g;
rbga[offset++] = b;
rbga[offset++] = a;
}
return rbga;
};
ImageDecoder.bgra4444 = function (data, width, height) {
var rgba = new Uint8Array(4 * width * height);
var offset = 0;
for (var i = 0; i < data.length; i += 2) {
var color = ImageDecoder.readUInt16LE(data, i);
var _a = ImageDecoder.decode4444(color), a = _a[0], r = _a[1], g = _a[2], b = _a[3];
rgba[offset++] = r;
rgba[offset++] = g;
rgba[offset++] = b;
rgba[offset++] = a;
}
return rgba;
};
ImageDecoder.bgra555 = function (data, width, height) {
var rgba = new Uint8Array(4 * width * height);
var offset = 0;
for (var i = 0; i < data.length; i += 2) {
var color = ImageDecoder.readUInt16LE(data, i);
var _a = ImageDecoder.decode555(color), r = _a[0], g = _a[1], b = _a[2];
rgba[offset++] = r;
rgba[offset++] = g;
rgba[offset++] = b;
rgba[offset++] = 0xff;
}
return rgba;
};
ImageDecoder.bgra565 = function (data, width, height) {
var rgba = new Uint8Array(4 * width * height);
var offset = 0;
for (var i = 0; i < data.length; i += 2) {
var color = ImageDecoder.readUInt16LE(data, i);
var _a = ImageDecoder.decode565(color), r = _a[0], g = _a[1], b = _a[2];
rgba[offset++] = r;
rgba[offset++] = g;
rgba[offset++] = b;
rgba[offset++] = 0xff;
}
return rgba;
};
ImageDecoder.bgra888 = function (data, width, height) {
var rgba = new Uint8Array(4 * width * height);
for (var i = 0; i < data.length; i += 4) {
rgba[i + 0] = data[i + 2];
rgba[i + 1] = data[i + 1];
rgba[i + 2] = data[i + 0];
rgba[i + 3] = 0xff;
}
return rgba;
};
ImageDecoder.bgra8888 = function (data, width, height) {
var rgba = new Uint8Array(4 * width * height);
for (var i = 0; i < data.length; i += 4) {
rgba[i + 0] = data[i + 2];
rgba[i + 1] = data[i + 1];
rgba[i + 2] = data[i + 0];
rgba[i + 3] = data[i + 3];
}
return rgba;
};
ImageDecoder.lum8 = function (data, width, height) {
var rgba = new Uint8Array(4 * width * height);
for (var i = 0; i < data.length; i++) {
var offset = i * 4;
var luminance = data[i];
rgba[offset + 0] = luminance; // R
rgba[offset + 1] = luminance; // G
rgba[offset + 2] = luminance; // B
rgba[offset + 3] = 0xff;
}
return rgba;
};
ImageDecoder.lum8a8 = function (data, width, height) {
var rgba = new Uint8Array(4 * width * height);
var offset = 0;
for (var i = 0; i < data.length; i += 2) {
var luminance = data[i];
var alpha = data[i + 1];
rgba[offset++] = luminance;
rgba[offset++] = luminance;
rgba[offset++] = luminance;
rgba[offset++] = alpha;
}
return rgba;
};
ImageDecoder.pal4 = function (data, palette, width, height) {
var rgba = new Uint8Array(4 * width * height);
var offset = 0;
for (var i = 0; i < data.length; i++) {
var b = data[i];
var idx1 = (b >> 4) & 0xf;
var idx2 = b & 0xf;
// Copying RGBA from the palette for two pixels
rgba[offset++] = palette[idx1 * 4 + 0]; // R
rgba[offset++] = palette[idx1 * 4 + 1]; // G
rgba[offset++] = palette[idx1 * 4 + 2]; // B
rgba[offset++] = palette[idx1 * 4 + 3]; // A
rgba[offset++] = palette[idx2 * 4 + 0]; // R
rgba[offset++] = palette[idx2 * 4 + 1]; // G
rgba[offset++] = palette[idx2 * 4 + 2]; // B
rgba[offset++] = palette[idx2 * 4 + 3]; // A
}
return rgba;
};
ImageDecoder.pal4NoAlpha = function (data, palette, width, height) {
var rgba = new Uint8Array(4 * width * height);
var offset = 0;
for (var i = 0; i < data.length; i++) {
var b = data[i];
var colorIndex1 = (b >> 4) & 0xf;
var colorIndex2 = b & 0xf;
// First pixel
rgba[offset++] = palette[colorIndex1 * 4 + 0]; // R
rgba[offset++] = palette[colorIndex1 * 4 + 1]; // G
rgba[offset++] = palette[colorIndex1 * 4 + 2]; // B
rgba[offset++] = 0xff;
// Second pixel
rgba[offset++] = palette[colorIndex2 * 4 + 0]; // R
rgba[offset++] = palette[colorIndex2 * 4 + 1]; // G
rgba[offset++] = palette[colorIndex2 * 4 + 2]; // B
rgba[offset++] = 0xff;
}
return rgba;
};
ImageDecoder.pal8 = function (data, palette, width, height) {
var rgba = new Uint8Array(4 * width * height);
for (var i = 0; i < data.length; i++) {
var colorIndex = data[i];
// Copy RGBA from palette
rgba[i * 4 + 0] = palette[colorIndex * 4 + 0]; // R
rgba[i * 4 + 1] = palette[colorIndex * 4 + 1]; // G
rgba[i * 4 + 2] = palette[colorIndex * 4 + 2]; // B
rgba[i * 4 + 3] = palette[colorIndex * 4 + 3]; // A
}
return rgba;
};
ImageDecoder.pal8NoAlpha = function (data, palette, width, height) {
var rgba = new Uint8Array(4 * width * height);
for (var i = 0; i < data.length; i++) {
var colorIndex = data[i];
// Copy RGB from palette
rgba[i * 4 + 0] = palette[colorIndex * 4 + 0]; // R
rgba[i * 4 + 1] = palette[colorIndex * 4 + 1]; // G
rgba[i * 4 + 2] = palette[colorIndex * 4 + 2]; // B
rgba[i * 4 + 3] = 0xff;
}
return rgba;
};
return ImageDecoder;
}());
exports.ImageDecoder = ImageDecoder;