UNPKG

rw-parser

Version:

Parses RenderWare DFF and TXD files into usable format!

513 lines (509 loc) 22.1 kB
"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;