mirror of
https://github.com/4sval/FModel.git
synced 2026-04-17 06:56:46 -05:00
skidded ASTC Texture support + fixed crash if no ufont
This commit is contained in:
parent
358b03de75
commit
f19033f04c
|
|
@ -63,7 +63,7 @@ namespace FModel.Creator.Texts
|
|||
if (Globals.Game.ActualGame == EGame.Fortnite)
|
||||
{
|
||||
ArraySegment<byte>[] t = Utils.GetPropertyArraySegmentByte(_FORTNITE_BASE_PATH + _BURBANK_BIG_CONDENSED_BLACK);
|
||||
if (t != null && t.Length == 3)
|
||||
if (t != null && t.Length == 3 && t[2].Array != null)
|
||||
BundleDefaultTypeface = SKTypeface.FromStream(t[2].AsStream());
|
||||
else BundleDefaultTypeface = DefaultTypeface;
|
||||
|
||||
|
|
@ -78,7 +78,7 @@ namespace FModel.Creator.Texts
|
|||
if (!namePath.Equals(_FORTNITE_BASE_PATH))
|
||||
{
|
||||
t = Utils.GetPropertyArraySegmentByte(namePath);
|
||||
if (t != null && t.Length == 3)
|
||||
if (t != null && t.Length == 3 && t[2].Array != null)
|
||||
DisplayNameTypeface = SKTypeface.FromStream(t[2].AsStream());
|
||||
}
|
||||
else DisplayNameTypeface = DefaultTypeface;
|
||||
|
|
@ -91,7 +91,7 @@ namespace FModel.Creator.Texts
|
|||
Properties.Settings.Default.AssetsLanguage == (long)ELanguage.Chinese ? _NOTO_SANS_SC_REGULAR :
|
||||
_NOTO_SANS_REGULAR);
|
||||
t = Utils.GetPropertyArraySegmentByte(descriptionPath);
|
||||
if (t != null && t.Length == 3)
|
||||
if (t != null && t.Length == 3 && t[2].Array != null)
|
||||
DescriptionTypeface = SKTypeface.FromStream(t[2].AsStream());
|
||||
else DescriptionTypeface = DefaultTypeface;
|
||||
|
||||
|
|
@ -106,7 +106,7 @@ namespace FModel.Creator.Texts
|
|||
if (!bundleNamePath.Equals(_FORTNITE_BASE_PATH))
|
||||
{
|
||||
t = Utils.GetPropertyArraySegmentByte(bundleNamePath);
|
||||
if (t != null && t.Length == 3)
|
||||
if (t != null && t.Length == 3 && t[2].Array != null)
|
||||
BundleDisplayNameTypeface = SKTypeface.FromStream(t[2].AsStream());
|
||||
}
|
||||
else BundleDisplayNameTypeface = BundleDefaultTypeface;
|
||||
|
|
@ -122,7 +122,7 @@ namespace FModel.Creator.Texts
|
|||
Properties.Settings.Default.AssetsLanguage == (long)ELanguage.Chinese ? _TUNGSTEN_SIMPLIFIED_CHINESE :
|
||||
_TUNGSTEN_BOLD);
|
||||
ArraySegment<byte>[] t = Utils.GetPropertyArraySegmentByte(namePath);
|
||||
if (t != null && t.Length == 3)
|
||||
if (t != null && t.Length == 3 && t[2].Array != null)
|
||||
DisplayNameTypeface = SKTypeface.FromStream(t[2].AsStream());
|
||||
else DisplayNameTypeface = DefaultTypeface;
|
||||
|
||||
|
|
@ -134,12 +134,12 @@ namespace FModel.Creator.Texts
|
|||
Properties.Settings.Default.AssetsLanguage == (long)ELanguage.Chinese ? _NOTOSANS_CJK_LIGHT :
|
||||
_DINNEXT_W1G_LIGHT);
|
||||
t = Utils.GetPropertyArraySegmentByte(descriptionPath);
|
||||
if (t != null && t.Length == 3)
|
||||
if (t != null && t.Length == 3 && t[2].Array != null)
|
||||
DescriptionTypeface = SKTypeface.FromStream(t[2].AsStream());
|
||||
else DescriptionTypeface = DefaultTypeface;
|
||||
|
||||
t = Utils.GetPropertyArraySegmentByte(_VALORANT_BASE_PATH + _DINNEXT_W1G_BOLD);
|
||||
if (t != null && t.Length == 3)
|
||||
if (t != null && t.Length == 3 && t[2].Array != null)
|
||||
BundleDefaultTypeface = SKTypeface.FromStream(t[2].AsStream());
|
||||
else BundleDefaultTypeface = DefaultTypeface;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,8 +20,11 @@ namespace PakReader.Parsers.Class
|
|||
string FontFilename = Path.GetFileName(str.Value);
|
||||
string folder = FModel.Properties.Settings.Default.OutputPath + "\\Fonts\\";
|
||||
|
||||
using var fileStream = new FileStream(folder + FontFilename, FileMode.Create, FileAccess.Write);
|
||||
ufont.CopyTo(fileStream);
|
||||
if (ufont != null)
|
||||
{
|
||||
using var fileStream = new FileStream(folder + FontFilename, FileMode.Create, FileAccess.Write);
|
||||
ufont.CopyTo(fileStream);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using PakReader.Parsers.Objects;
|
||||
using PakReader.Textures;
|
||||
using SkiaSharp;
|
||||
|
||||
namespace PakReader.Parsers.Class
|
||||
|
|
|
|||
|
|
@ -1,432 +0,0 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using PakReader.Parsers.Objects;
|
||||
using SkiaSharp;
|
||||
|
||||
namespace PakReader
|
||||
{
|
||||
static class TextureDecoder
|
||||
{
|
||||
public static SKImage DecodeImage(byte[] sequence, int width, int height, int depth, EPixelFormat format)
|
||||
{
|
||||
byte[] data;
|
||||
SKColorType colorType;
|
||||
switch (format)
|
||||
{
|
||||
case EPixelFormat.PF_DXT5:
|
||||
data = DXTDecoder.DecodeDXT5(sequence, width, height, depth);
|
||||
colorType = SKColorType.Rgba8888;
|
||||
break;
|
||||
case EPixelFormat.PF_DXT1:
|
||||
data = DXTDecoder.DecodeDXT1(sequence, width, height, depth);
|
||||
colorType = SKColorType.Rgba8888;
|
||||
break;
|
||||
case EPixelFormat.PF_B8G8R8A8:
|
||||
data = sequence;
|
||||
colorType = SKColorType.Bgra8888;
|
||||
break;
|
||||
case EPixelFormat.PF_BC5:
|
||||
data = BCDecoder.DecodeBC5(sequence, width, height);
|
||||
colorType = SKColorType.Bgra8888;
|
||||
break;
|
||||
case EPixelFormat.PF_BC4:
|
||||
data = BCDecoder.DecodeBC4(sequence, width, height);
|
||||
colorType = SKColorType.Bgra8888;
|
||||
break;
|
||||
case EPixelFormat.PF_G8:
|
||||
data = sequence;
|
||||
colorType = SKColorType.Gray8;
|
||||
break;
|
||||
case EPixelFormat.PF_FloatRGBA:
|
||||
data = sequence;
|
||||
colorType = SKColorType.RgbaF16;
|
||||
break;
|
||||
default:
|
||||
throw new NotImplementedException($"Cannot decode {format} format");
|
||||
}
|
||||
|
||||
using var bitmap = new SKBitmap(new SKImageInfo(width, height, colorType, SKAlphaType.Unpremul));
|
||||
unsafe
|
||||
{
|
||||
fixed (byte* p = data)
|
||||
{
|
||||
bitmap.SetPixels(new IntPtr(p));
|
||||
}
|
||||
}
|
||||
return SKImage.FromBitmap(bitmap);
|
||||
}
|
||||
|
||||
static class BCDecoder
|
||||
{
|
||||
public static byte[] DecodeBC4(byte[] inp, int width, int height)
|
||||
{
|
||||
byte[] ret = new byte[width * height * 4];
|
||||
using var reader = new BinaryReader(new MemoryStream(inp));
|
||||
for (int y_block = 0; y_block < height / 4; y_block++)
|
||||
{
|
||||
for (int x_block = 0; x_block < width / 4; x_block++)
|
||||
{
|
||||
var r_bytes = DecodeBC3Block(reader);
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
ret[GetPixelLoc(width, x_block * 4 + (i % 4), y_block * 4 + (i / 4), 4, 0)] = r_bytes[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
public static byte[] DecodeBC5(byte[] inp, int width, int height)
|
||||
{
|
||||
byte[] ret = new byte[width * height * 4];
|
||||
using var reader = new BinaryReader(new MemoryStream(inp));
|
||||
for (int y_block = 0; y_block < height / 4; y_block++)
|
||||
{
|
||||
for (int x_block = 0; x_block < width / 4; x_block++)
|
||||
{
|
||||
var r_bytes = DecodeBC3Block(reader);
|
||||
var g_bytes = DecodeBC3Block(reader);
|
||||
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
ret[GetPixelLoc(width, x_block * 4 + (i % 4), y_block * 4 + (i / 4), 4, 0)] = r_bytes[i];
|
||||
ret[GetPixelLoc(width, x_block * 4 + (i % 4), y_block * 4 + (i / 4), 4, 1)] = g_bytes[i];
|
||||
ret[GetPixelLoc(width, x_block * 4 + (i % 4), y_block * 4 + (i / 4), 4, 2)] = GetZNormal(r_bytes[i], g_bytes[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int GetPixelLoc(int width, int x, int y, int bpp, int off) => (y * width + x) * bpp + off;
|
||||
|
||||
static byte GetZNormal(byte x, byte y)
|
||||
{
|
||||
var xf = (x / 127.5f) - 1;
|
||||
var yf = (y / 127.5f) - 1;
|
||||
var zval = 1 - xf * xf - yf * yf;
|
||||
var zval_ = (float)Math.Sqrt(zval > 0 ? zval : 0);
|
||||
zval = zval_ < 1 ? zval_ : 1;
|
||||
return (byte)((zval * 127) + 128);
|
||||
}
|
||||
|
||||
static byte[] DecodeBC3Block(BinaryReader reader)
|
||||
{
|
||||
float ref0 = reader.ReadByte();
|
||||
float ref1 = reader.ReadByte();
|
||||
|
||||
float[] ref_sl = new float[8];
|
||||
ref_sl[0] = ref0;
|
||||
ref_sl[1] = ref1;
|
||||
|
||||
if (ref0 > ref1)
|
||||
{
|
||||
ref_sl[2] = (6 * ref0 + 1 * ref1) / 7;
|
||||
ref_sl[3] = (5 * ref0 + 2 * ref1) / 7;
|
||||
ref_sl[4] = (4 * ref0 + 3 * ref1) / 7;
|
||||
ref_sl[5] = (3 * ref0 + 4 * ref1) / 7;
|
||||
ref_sl[6] = (2 * ref0 + 5 * ref1) / 7;
|
||||
ref_sl[7] = (1 * ref0 + 6 * ref1) / 7;
|
||||
}
|
||||
else
|
||||
{
|
||||
ref_sl[2] = (4 * ref0 + 1 * ref1) / 5;
|
||||
ref_sl[3] = (3 * ref0 + 2 * ref1) / 5;
|
||||
ref_sl[4] = (2 * ref0 + 3 * ref1) / 5;
|
||||
ref_sl[5] = (1 * ref0 + 4 * ref1) / 5;
|
||||
ref_sl[6] = 0;
|
||||
ref_sl[7] = 255;
|
||||
}
|
||||
|
||||
byte[] index_block1 = GetBC3Indices(reader.ReadBytes(3));
|
||||
|
||||
byte[] index_block2 = GetBC3Indices(reader.ReadBytes(3));
|
||||
|
||||
byte[] bytes = new byte[16];
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
bytes[7 - i] = (byte)ref_sl[index_block1[i]];
|
||||
}
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
bytes[15 - i] = (byte)ref_sl[index_block2[i]];
|
||||
}
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
static byte[] GetBC3Indices(byte[] buf_block) =>
|
||||
new byte[] {
|
||||
(byte)((buf_block[2] & 0b1110_0000) >> 5),
|
||||
(byte)((buf_block[2] & 0b0001_1100) >> 2),
|
||||
(byte)(((buf_block[2] & 0b0000_0011) << 1) | ((buf_block[1] & 0b1 << 7) >> 7)),
|
||||
(byte)((buf_block[1] & 0b0111_0000) >> 4),
|
||||
(byte)((buf_block[1] & 0b0000_1110) >> 1),
|
||||
(byte)(((buf_block[1] & 0b0000_0001) << 2) | ((buf_block[0] & 0b11 << 6) >> 6)),
|
||||
(byte)((buf_block[0] & 0b0011_1000) >> 3),
|
||||
(byte)(buf_block[0] & 0b0000_0111)
|
||||
};
|
||||
}
|
||||
|
||||
static class DXTDecoder
|
||||
{
|
||||
struct Colour8888
|
||||
{
|
||||
public byte red;
|
||||
public byte green;
|
||||
public byte blue;
|
||||
public byte alpha;
|
||||
}
|
||||
|
||||
public static byte[] DecodeDXT1(byte[] inp, int width, int height, int depth)
|
||||
{
|
||||
var bpp = 4;
|
||||
var bps = width * bpp * 1;
|
||||
var sizeofplane = bps * height;
|
||||
|
||||
byte[] rawData = new byte[depth * sizeofplane + height * bps + width * bpp];
|
||||
var colours = new Colour8888[4];
|
||||
colours[0].alpha = 0xFF;
|
||||
colours[1].alpha = 0xFF;
|
||||
colours[2].alpha = 0xFF;
|
||||
|
||||
unsafe
|
||||
{
|
||||
fixed (byte* bytePtr = inp)
|
||||
{
|
||||
byte* temp = bytePtr;
|
||||
for (int z = 0; z < depth; z++)
|
||||
{
|
||||
for (int y = 0; y < height; y += 4)
|
||||
{
|
||||
for (int x = 0; x < width; x += 4)
|
||||
{
|
||||
ushort colour0 = *((ushort*)temp);
|
||||
ushort colour1 = *((ushort*)(temp + 2));
|
||||
DxtcReadColor(colour0, ref colours[0]);
|
||||
DxtcReadColor(colour1, ref colours[1]);
|
||||
|
||||
uint bitmask = ((uint*)temp)[1];
|
||||
temp += 8;
|
||||
|
||||
if (colour0 > colour1)
|
||||
{
|
||||
// Four-color block: derive the other two colors.
|
||||
// 00 = color_0, 01 = color_1, 10 = color_2, 11 = color_3
|
||||
// These 2-bit codes correspond to the 2-bit fields
|
||||
// stored in the 64-bit block.
|
||||
colours[2].blue = (byte)((2 * colours[0].blue + colours[1].blue + 1) / 3);
|
||||
colours[2].green = (byte)((2 * colours[0].green + colours[1].green + 1) / 3);
|
||||
colours[2].red = (byte)((2 * colours[0].red + colours[1].red + 1) / 3);
|
||||
//colours[2].alpha = 0xFF;
|
||||
|
||||
colours[3].blue = (byte)((colours[0].blue + 2 * colours[1].blue + 1) / 3);
|
||||
colours[3].green = (byte)((colours[0].green + 2 * colours[1].green + 1) / 3);
|
||||
colours[3].red = (byte)((colours[0].red + 2 * colours[1].red + 1) / 3);
|
||||
colours[3].alpha = 0xFF;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Three-color block: derive the other color.
|
||||
// 00 = color_0, 01 = color_1, 10 = color_2,
|
||||
// 11 = transparent.
|
||||
// These 2-bit codes correspond to the 2-bit fields
|
||||
// stored in the 64-bit block.
|
||||
colours[2].blue = (byte)((colours[0].blue + colours[1].blue) / 2);
|
||||
colours[2].green = (byte)((colours[0].green + colours[1].green) / 2);
|
||||
colours[2].red = (byte)((colours[0].red + colours[1].red) / 2);
|
||||
//colours[2].alpha = 0xFF;
|
||||
|
||||
colours[3].blue = (byte)((colours[0].blue + 2 * colours[1].blue + 1) / 3);
|
||||
colours[3].green = (byte)((colours[0].green + 2 * colours[1].green + 1) / 3);
|
||||
colours[3].red = (byte)((colours[0].red + 2 * colours[1].red + 1) / 3);
|
||||
colours[3].alpha = 0x00;
|
||||
}
|
||||
|
||||
for (int j = 0, k = 0; j < 4; j++)
|
||||
{
|
||||
for (int i = 0; i < 4; i++, k++)
|
||||
{
|
||||
int select = (int)((bitmask & (0x03 << k * 2)) >> k * 2);
|
||||
Colour8888 col = colours[select];
|
||||
if (((x + i) < width) && ((y + j) < height))
|
||||
{
|
||||
uint offset = (uint)(z * sizeofplane + (y + j) * bps + (x + i) * bpp);
|
||||
rawData[offset + 0] = col.red;
|
||||
rawData[offset + 1] = col.green;
|
||||
rawData[offset + 2] = col.blue;
|
||||
rawData[offset + 3] = col.alpha;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return rawData;
|
||||
}
|
||||
|
||||
public static byte[] DecodeDXT5(byte[] inp, int width, int height, int depth)
|
||||
{
|
||||
var bpp = 4;
|
||||
var bps = width * bpp * 1;
|
||||
var sizeofplane = bps * height;
|
||||
|
||||
byte[] rawData = new byte[depth * sizeofplane + height * bps + width * bpp];
|
||||
var colours = new Colour8888[4];
|
||||
ushort[] alphas = new ushort[8];
|
||||
|
||||
unsafe
|
||||
{
|
||||
fixed (byte* bytePtr = inp)
|
||||
{
|
||||
byte* temp = bytePtr;
|
||||
for (int z = 0; z < depth; z++)
|
||||
{
|
||||
for (int y = 0; y < height; y += 4)
|
||||
{
|
||||
for (int x = 0; x < width; x += 4)
|
||||
{
|
||||
if (y >= height || x >= width)
|
||||
break;
|
||||
|
||||
alphas[0] = temp[0];
|
||||
alphas[1] = temp[1];
|
||||
byte* alphamask = (temp + 2);
|
||||
temp += 8;
|
||||
|
||||
DxtcReadColors(temp, colours);
|
||||
uint bitmask = ((uint*)temp)[1];
|
||||
temp += 8;
|
||||
|
||||
// Four-color block: derive the other two colors.
|
||||
// 00 = color_0, 01 = color_1, 10 = color_2, 11 = color_3
|
||||
// These 2-bit codes correspond to the 2-bit fields
|
||||
// stored in the 64-bit block.
|
||||
colours[2].blue = (byte)((2 * colours[0].blue + colours[1].blue + 1) / 3);
|
||||
colours[2].green = (byte)((2 * colours[0].green + colours[1].green + 1) / 3);
|
||||
colours[2].red = (byte)((2 * colours[0].red + colours[1].red + 1) / 3);
|
||||
//colours[2].alpha = 0xFF;
|
||||
|
||||
colours[3].blue = (byte)((colours[0].blue + 2 * colours[1].blue + 1) / 3);
|
||||
colours[3].green = (byte)((colours[0].green + 2 * colours[1].green + 1) / 3);
|
||||
colours[3].red = (byte)((colours[0].red + 2 * colours[1].red + 1) / 3);
|
||||
//colours[3].alpha = 0xFF;
|
||||
|
||||
int k = 0;
|
||||
for (int j = 0; j < 4; j++)
|
||||
{
|
||||
for (int i = 0; i < 4; k++, i++)
|
||||
{
|
||||
int select = (int)((bitmask & (0x03 << k * 2)) >> k * 2);
|
||||
Colour8888 col = colours[select];
|
||||
// only put pixels out < width or height
|
||||
if (((x + i) < width) && ((y + j) < height))
|
||||
{
|
||||
uint offset = (uint)(z * sizeofplane + (y + j) * bps + (x + i) * bpp);
|
||||
rawData[offset] = col.red;
|
||||
rawData[offset + 1] = col.green;
|
||||
rawData[offset + 2] = col.blue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 8-alpha or 6-alpha block?
|
||||
if (alphas[0] > alphas[1])
|
||||
{
|
||||
// 8-alpha block: derive the other six alphas.
|
||||
// Bit code 000 = alpha_0, 001 = alpha_1, others are interpolated.
|
||||
alphas[2] = (ushort)((6 * alphas[0] + 1 * alphas[1] + 3) / 7); // bit code 010
|
||||
alphas[3] = (ushort)((5 * alphas[0] + 2 * alphas[1] + 3) / 7); // bit code 011
|
||||
alphas[4] = (ushort)((4 * alphas[0] + 3 * alphas[1] + 3) / 7); // bit code 100
|
||||
alphas[5] = (ushort)((3 * alphas[0] + 4 * alphas[1] + 3) / 7); // bit code 101
|
||||
alphas[6] = (ushort)((2 * alphas[0] + 5 * alphas[1] + 3) / 7); // bit code 110
|
||||
alphas[7] = (ushort)((1 * alphas[0] + 6 * alphas[1] + 3) / 7); // bit code 111
|
||||
}
|
||||
else
|
||||
{
|
||||
// 6-alpha block.
|
||||
// Bit code 000 = alpha_0, 001 = alpha_1, others are interpolated.
|
||||
alphas[2] = (ushort)((4 * alphas[0] + 1 * alphas[1] + 2) / 5); // Bit code 010
|
||||
alphas[3] = (ushort)((3 * alphas[0] + 2 * alphas[1] + 2) / 5); // Bit code 011
|
||||
alphas[4] = (ushort)((2 * alphas[0] + 3 * alphas[1] + 2) / 5); // Bit code 100
|
||||
alphas[5] = (ushort)((1 * alphas[0] + 4 * alphas[1] + 2) / 5); // Bit code 101
|
||||
alphas[6] = 0x00; // Bit code 110
|
||||
alphas[7] = 0xFF; // Bit code 111
|
||||
}
|
||||
|
||||
// Note: Have to separate the next two loops,
|
||||
// it operates on a 6-byte system.
|
||||
|
||||
// First three bytes
|
||||
//uint bits = (uint)(alphamask[0]);
|
||||
uint bits = (uint)((alphamask[0]) | (alphamask[1] << 8) | (alphamask[2] << 16));
|
||||
for (int j = 0; j < 2; j++)
|
||||
{
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
// only put pixels out < width or height
|
||||
if (((x + i) < width) && ((y + j) < height))
|
||||
{
|
||||
uint offset = (uint)(z * sizeofplane + (y + j) * bps + (x + i) * bpp + 3);
|
||||
rawData[offset] = (byte)alphas[bits & 0x07];
|
||||
}
|
||||
bits >>= 3;
|
||||
}
|
||||
}
|
||||
|
||||
// Last three bytes
|
||||
//bits = (uint)(alphamask[3]);
|
||||
bits = (uint)((alphamask[3]) | (alphamask[4] << 8) | (alphamask[5] << 16));
|
||||
for (int j = 2; j < 4; j++)
|
||||
{
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
// only put pixels out < width or height
|
||||
if (((x + i) < width) && ((y + j) < height))
|
||||
{
|
||||
uint offset = (uint)(z * sizeofplane + (y + j) * bps + (x + i) * bpp + 3);
|
||||
rawData[offset] = (byte)alphas[bits & 0x07];
|
||||
}
|
||||
bits >>= 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return rawData;
|
||||
}
|
||||
}
|
||||
|
||||
static unsafe void DxtcReadColors(byte* data, Colour8888[] op)
|
||||
{
|
||||
byte buf = (byte)((data[1] & 0xF8) >> 3);
|
||||
op[0].red = (byte)(buf << 3 | buf >> 2);
|
||||
buf = (byte)(((data[0] & 0xE0) >> 5) | ((data[1] & 0x7) << 3));
|
||||
op[0].green = (byte)(buf << 2 | buf >> 3);
|
||||
buf = (byte)(data[0] & 0x1F);
|
||||
op[0].blue = (byte)(buf << 3 | buf >> 2);
|
||||
|
||||
buf = (byte)((data[3] & 0xF8) >> 3);
|
||||
op[1].red = (byte)(buf << 3 | buf >> 2);
|
||||
buf = (byte)(((data[2] & 0xE0) >> 5) | ((data[3] & 0x7) << 3));
|
||||
op[1].green = (byte)(buf << 2 | buf >> 3);
|
||||
buf = (byte)(data[2] & 0x1F);
|
||||
op[1].blue = (byte)(buf << 3 | buf >> 2);
|
||||
}
|
||||
|
||||
static void DxtcReadColor(ushort data, ref Colour8888 op)
|
||||
{
|
||||
byte buf = (byte)((data & 0xF800) >> 11);
|
||||
op.red = (byte)(buf << 3 | buf >> 2);
|
||||
buf = (byte)((data & 0x7E0) >> 5);
|
||||
op.green = (byte)(buf << 2 | buf >> 3);
|
||||
buf = (byte)(data & 0x1f);
|
||||
op.blue = (byte)(buf << 3 | buf >> 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
1338
FModel/PakReader/Textures/ASTC/ASTCDecoder.cs
Normal file
1338
FModel/PakReader/Textures/ASTC/ASTCDecoder.cs
Normal file
File diff suppressed because it is too large
Load Diff
133
FModel/PakReader/Textures/ASTC/ASTCPixel.cs
Normal file
133
FModel/PakReader/Textures/ASTC/ASTCPixel.cs
Normal file
|
|
@ -0,0 +1,133 @@
|
|||
using System;
|
||||
|
||||
namespace PakReader.Textures.ASTC
|
||||
{
|
||||
class ASTCPixel
|
||||
{
|
||||
public short R { get; set; }
|
||||
public short G { get; set; }
|
||||
public short B { get; set; }
|
||||
public short A { get; set; }
|
||||
|
||||
readonly byte[] BitDepth = new byte[4];
|
||||
|
||||
public ASTCPixel(short _A, short _R, short _G, short _B)
|
||||
{
|
||||
A = _A;
|
||||
R = _R;
|
||||
G = _G;
|
||||
B = _B;
|
||||
|
||||
for (int i = 0; i < 4; i++)
|
||||
BitDepth[i] = 8;
|
||||
}
|
||||
|
||||
public void ClampByte()
|
||||
{
|
||||
R = Math.Min(Math.Max(R, (short)0), (short)255);
|
||||
G = Math.Min(Math.Max(G, (short)0), (short)255);
|
||||
B = Math.Min(Math.Max(B, (short)0), (short)255);
|
||||
A = Math.Min(Math.Max(A, (short)0), (short)255);
|
||||
}
|
||||
|
||||
public short GetComponent(int Index)
|
||||
{
|
||||
return Index switch
|
||||
{
|
||||
0 => A,
|
||||
1 => R,
|
||||
2 => G,
|
||||
3 => B,
|
||||
_ => 0,
|
||||
};
|
||||
}
|
||||
|
||||
public void SetComponent(int Index, int Value)
|
||||
{
|
||||
switch (Index)
|
||||
{
|
||||
case 0:
|
||||
A = (short)Value;
|
||||
break;
|
||||
case 1:
|
||||
R = (short)Value;
|
||||
break;
|
||||
case 2:
|
||||
G = (short)Value;
|
||||
break;
|
||||
case 3:
|
||||
B = (short)Value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public void ChangeBitDepth(byte[] Depth)
|
||||
{
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
int Value = ChangeBitDepth(GetComponent(i), BitDepth[i], Depth[i]);
|
||||
|
||||
SetComponent(i, Value);
|
||||
BitDepth[i] = Depth[i];
|
||||
}
|
||||
}
|
||||
|
||||
short ChangeBitDepth(short Value, byte OldDepth, byte NewDepth)
|
||||
{
|
||||
if (OldDepth == NewDepth)
|
||||
{
|
||||
// Do nothing
|
||||
return Value;
|
||||
}
|
||||
else if (OldDepth == 0 && NewDepth != 0)
|
||||
{
|
||||
return (short)((1 << NewDepth) - 1);
|
||||
}
|
||||
else if (NewDepth > OldDepth)
|
||||
{
|
||||
return (short)BitArrayStream.Replicate(Value, OldDepth, NewDepth);
|
||||
}
|
||||
else
|
||||
{
|
||||
// oldDepth > newDepth
|
||||
if (NewDepth == 0)
|
||||
{
|
||||
return 0xFF;
|
||||
}
|
||||
else
|
||||
{
|
||||
byte BitsWasted = (byte)(OldDepth - NewDepth);
|
||||
short TempValue = Value;
|
||||
|
||||
TempValue = (short)((TempValue + (1 << (BitsWasted - 1))) >> BitsWasted);
|
||||
TempValue = Math.Min(Math.Max((short)0, TempValue), (short)((1 << NewDepth) - 1));
|
||||
|
||||
return (byte)(TempValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int Pack()
|
||||
{
|
||||
ASTCPixel NewPixel = new ASTCPixel(A, R, G, B);
|
||||
byte[] eightBitDepth = { 8, 8, 8, 8 };
|
||||
|
||||
NewPixel.ChangeBitDepth(eightBitDepth);
|
||||
|
||||
return (byte)NewPixel.A << 24 |
|
||||
(byte)NewPixel.B << 16 |
|
||||
(byte)NewPixel.G << 8 |
|
||||
(byte)NewPixel.R << 0;
|
||||
}
|
||||
|
||||
// Adds more precision to the blue channel as described
|
||||
// in C.2.14
|
||||
public static ASTCPixel BlueContract(int a, int r, int g, int b)
|
||||
{
|
||||
return new ASTCPixel((short)(a),
|
||||
(short)((r + b) >> 1),
|
||||
(short)((g + b) >> 1),
|
||||
(short)(b));
|
||||
}
|
||||
}
|
||||
}
|
||||
120
FModel/PakReader/Textures/ASTC/BitArrayStream.cs
Normal file
120
FModel/PakReader/Textures/ASTC/BitArrayStream.cs
Normal file
|
|
@ -0,0 +1,120 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
|
||||
namespace PakReader.Textures.ASTC
|
||||
{
|
||||
public class BitArrayStream
|
||||
{
|
||||
public BitArray BitsArray;
|
||||
public int Position { get; private set; }
|
||||
|
||||
public BitArrayStream(BitArray BitArray)
|
||||
{
|
||||
BitsArray = BitArray;
|
||||
Position = 0;
|
||||
}
|
||||
|
||||
public short ReadBits(int Length)
|
||||
{
|
||||
int RetValue = 0;
|
||||
for (int i = Position; i < Position + Length; i++)
|
||||
{
|
||||
if (BitsArray[i])
|
||||
{
|
||||
RetValue |= 1 << (i - Position);
|
||||
}
|
||||
}
|
||||
|
||||
Position += Length;
|
||||
return (short)RetValue;
|
||||
}
|
||||
|
||||
public int ReadBits(int Start, int End)
|
||||
{
|
||||
int RetValue = 0;
|
||||
for (int i = Start; i <= End; i++)
|
||||
{
|
||||
if (BitsArray[i])
|
||||
{
|
||||
RetValue |= 1 << (i - Start);
|
||||
}
|
||||
}
|
||||
|
||||
return RetValue;
|
||||
}
|
||||
|
||||
public int ReadBit(int Index)
|
||||
{
|
||||
return Convert.ToInt32(BitsArray[Index]);
|
||||
}
|
||||
|
||||
public void WriteBits(int Value, int Length)
|
||||
{
|
||||
for (int i = Position; i < Position + Length; i++)
|
||||
{
|
||||
BitsArray[i] = ((Value >> (i - Position)) & 1) != 0;
|
||||
}
|
||||
|
||||
Position += Length;
|
||||
}
|
||||
|
||||
public byte[] ToByteArray()
|
||||
{
|
||||
byte[] RetArray = new byte[(BitsArray.Length + 7) / 8];
|
||||
BitsArray.CopyTo(RetArray, 0);
|
||||
return RetArray;
|
||||
}
|
||||
|
||||
public static int Replicate(int Value, int NumberBits, int ToBit)
|
||||
{
|
||||
if (NumberBits == 0) return 0;
|
||||
if (ToBit == 0) return 0;
|
||||
|
||||
int TempValue = Value & ((1 << NumberBits) - 1);
|
||||
int RetValue = TempValue;
|
||||
int ResLength = NumberBits;
|
||||
|
||||
while (ResLength < ToBit)
|
||||
{
|
||||
int Comp = 0;
|
||||
if (NumberBits > ToBit - ResLength)
|
||||
{
|
||||
int NewShift = ToBit - ResLength;
|
||||
Comp = NumberBits - NewShift;
|
||||
NumberBits = NewShift;
|
||||
}
|
||||
RetValue <<= NumberBits;
|
||||
RetValue |= TempValue >> Comp;
|
||||
ResLength += NumberBits;
|
||||
}
|
||||
return RetValue;
|
||||
}
|
||||
|
||||
public static int PopCnt(int Number)
|
||||
{
|
||||
int Counter;
|
||||
for (Counter = 0; Number != 0; Counter++)
|
||||
{
|
||||
Number &= Number - 1;
|
||||
}
|
||||
return Counter;
|
||||
}
|
||||
|
||||
public static void Swap<T>(ref T lhs, ref T rhs)
|
||||
{
|
||||
T Temp = lhs;
|
||||
lhs = rhs;
|
||||
rhs = Temp;
|
||||
}
|
||||
|
||||
// Transfers a bit as described in C.2.14
|
||||
public static void BitTransferSigned(ref int a, ref int b)
|
||||
{
|
||||
b >>= 1;
|
||||
b |= a & 0x80;
|
||||
a >>= 1;
|
||||
a &= 0x3F;
|
||||
if ((a & 0x20) != 0) a -= 0x40;
|
||||
}
|
||||
}
|
||||
}
|
||||
268
FModel/PakReader/Textures/ASTC/IntegerEncoded.cs
Normal file
268
FModel/PakReader/Textures/ASTC/IntegerEncoded.cs
Normal file
|
|
@ -0,0 +1,268 @@
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace PakReader.Textures.ASTC
|
||||
{
|
||||
public struct IntegerEncoded
|
||||
{
|
||||
public enum EIntegerEncoding
|
||||
{
|
||||
JustBits,
|
||||
Quint,
|
||||
Trit
|
||||
}
|
||||
|
||||
readonly EIntegerEncoding Encoding;
|
||||
public int NumberBits { get; private set; }
|
||||
public int BitValue { get; private set; }
|
||||
public int TritValue { get; private set; }
|
||||
public int QuintValue { get; private set; }
|
||||
|
||||
public IntegerEncoded(EIntegerEncoding _Encoding, int NumBits)
|
||||
{
|
||||
Encoding = _Encoding;
|
||||
NumberBits = NumBits;
|
||||
BitValue = 0;
|
||||
TritValue = 0;
|
||||
QuintValue = 0;
|
||||
}
|
||||
|
||||
public bool MatchesEncoding(IntegerEncoded Other)
|
||||
{
|
||||
return Encoding == Other.Encoding && NumberBits == Other.NumberBits;
|
||||
}
|
||||
|
||||
public EIntegerEncoding GetEncoding()
|
||||
{
|
||||
return Encoding;
|
||||
}
|
||||
|
||||
public int GetBitLength(int NumberVals)
|
||||
{
|
||||
int TotalBits = NumberBits * NumberVals;
|
||||
if (Encoding == EIntegerEncoding.Trit)
|
||||
{
|
||||
TotalBits += (NumberVals * 8 + 4) / 5;
|
||||
}
|
||||
else if (Encoding == EIntegerEncoding.Quint)
|
||||
{
|
||||
TotalBits += (NumberVals * 7 + 2) / 3;
|
||||
}
|
||||
return TotalBits;
|
||||
}
|
||||
|
||||
public static IntegerEncoded CreateEncoding(int MaxVal)
|
||||
{
|
||||
while (MaxVal > 0)
|
||||
{
|
||||
int Check = MaxVal + 1;
|
||||
|
||||
// Is maxVal a power of two?
|
||||
if ((Check & (Check - 1)) == 0)
|
||||
{
|
||||
return new IntegerEncoded(EIntegerEncoding.JustBits, BitArrayStream.PopCnt(MaxVal));
|
||||
}
|
||||
|
||||
// Is maxVal of the type 3*2^n - 1?
|
||||
if ((Check % 3 == 0) && ((Check / 3) & ((Check / 3) - 1)) == 0)
|
||||
{
|
||||
return new IntegerEncoded(EIntegerEncoding.Trit, BitArrayStream.PopCnt(Check / 3 - 1));
|
||||
}
|
||||
|
||||
// Is maxVal of the type 5*2^n - 1?
|
||||
if ((Check % 5 == 0) && ((Check / 5) & ((Check / 5) - 1)) == 0)
|
||||
{
|
||||
return new IntegerEncoded(EIntegerEncoding.Quint, BitArrayStream.PopCnt(Check / 5 - 1));
|
||||
}
|
||||
|
||||
// Apparently it can't be represented with a bounded integer sequence...
|
||||
// just iterate.
|
||||
MaxVal--;
|
||||
}
|
||||
|
||||
return new IntegerEncoded(EIntegerEncoding.JustBits, 0);
|
||||
}
|
||||
|
||||
public static void DecodeTritBlock(
|
||||
BitArrayStream BitStream,
|
||||
List<IntegerEncoded> ListIntegerEncoded,
|
||||
int NumberBitsPerValue)
|
||||
{
|
||||
// Implement the algorithm in section C.2.12
|
||||
int[] m = new int[5];
|
||||
int[] t = new int[5];
|
||||
int T;
|
||||
|
||||
// Read the trit encoded block according to
|
||||
// table C.2.14
|
||||
m[0] = BitStream.ReadBits(NumberBitsPerValue);
|
||||
T = BitStream.ReadBits(2);
|
||||
m[1] = BitStream.ReadBits(NumberBitsPerValue);
|
||||
T |= BitStream.ReadBits(2) << 2;
|
||||
m[2] = BitStream.ReadBits(NumberBitsPerValue);
|
||||
T |= BitStream.ReadBits(1) << 4;
|
||||
m[3] = BitStream.ReadBits(NumberBitsPerValue);
|
||||
T |= BitStream.ReadBits(2) << 5;
|
||||
m[4] = BitStream.ReadBits(NumberBitsPerValue);
|
||||
T |= BitStream.ReadBits(1) << 7;
|
||||
|
||||
int C;
|
||||
BitArrayStream Tb = new BitArrayStream(new BitArray(new int[] { T }));
|
||||
if (Tb.ReadBits(2, 4) == 7)
|
||||
{
|
||||
C = (Tb.ReadBits(5, 7) << 2) | Tb.ReadBits(0, 1);
|
||||
t[4] = t[3] = 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
C = Tb.ReadBits(0, 4);
|
||||
if (Tb.ReadBits(5, 6) == 3)
|
||||
{
|
||||
t[4] = 2;
|
||||
t[3] = Tb.ReadBit(7);
|
||||
}
|
||||
else
|
||||
{
|
||||
t[4] = Tb.ReadBit(7);
|
||||
t[3] = Tb.ReadBits(5, 6);
|
||||
}
|
||||
}
|
||||
|
||||
BitArrayStream Cb = new BitArrayStream(new BitArray(new int[] { C }));
|
||||
if (Cb.ReadBits(0, 1) == 3)
|
||||
{
|
||||
t[2] = 2;
|
||||
t[1] = Cb.ReadBit(4);
|
||||
t[0] = (Cb.ReadBit(3) << 1) | (Cb.ReadBit(2) & ~Cb.ReadBit(3));
|
||||
}
|
||||
else if (Cb.ReadBits(2, 3) == 3)
|
||||
{
|
||||
t[2] = 2;
|
||||
t[1] = 2;
|
||||
t[0] = Cb.ReadBits(0, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
t[2] = Cb.ReadBit(4);
|
||||
t[1] = Cb.ReadBits(2, 3);
|
||||
t[0] = (Cb.ReadBit(1) << 1) | (Cb.ReadBit(0) & ~Cb.ReadBit(1));
|
||||
}
|
||||
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
IntegerEncoded IntEncoded = new IntegerEncoded(EIntegerEncoding.Trit, NumberBitsPerValue)
|
||||
{
|
||||
BitValue = m[i],
|
||||
TritValue = t[i]
|
||||
};
|
||||
ListIntegerEncoded.Add(IntEncoded);
|
||||
}
|
||||
}
|
||||
|
||||
public static void DecodeQuintBlock(
|
||||
BitArrayStream BitStream,
|
||||
List<IntegerEncoded> ListIntegerEncoded,
|
||||
int NumberBitsPerValue)
|
||||
{
|
||||
// Implement the algorithm in section C.2.12
|
||||
int[] m = new int[3];
|
||||
int[] q = new int[3];
|
||||
int Q;
|
||||
|
||||
// Read the trit encoded block according to
|
||||
// table C.2.15
|
||||
m[0] = BitStream.ReadBits(NumberBitsPerValue);
|
||||
Q = BitStream.ReadBits(3);
|
||||
m[1] = BitStream.ReadBits(NumberBitsPerValue);
|
||||
Q |= BitStream.ReadBits(2) << 3;
|
||||
m[2] = BitStream.ReadBits(NumberBitsPerValue);
|
||||
Q |= BitStream.ReadBits(2) << 5;
|
||||
|
||||
BitArrayStream Qb = new BitArrayStream(new BitArray(new int[] { Q }));
|
||||
if (Qb.ReadBits(1, 2) == 3 && Qb.ReadBits(5, 6) == 0)
|
||||
{
|
||||
q[0] = q[1] = 4;
|
||||
q[2] = (Qb.ReadBit(0) << 2) | ((Qb.ReadBit(4) & ~Qb.ReadBit(0)) << 1) | (Qb.ReadBit(3) & ~Qb.ReadBit(0));
|
||||
}
|
||||
else
|
||||
{
|
||||
int C;
|
||||
if (Qb.ReadBits(1, 2) == 3)
|
||||
{
|
||||
q[2] = 4;
|
||||
C = (Qb.ReadBits(3, 4) << 3) | ((~Qb.ReadBits(5, 6) & 3) << 1) | Qb.ReadBit(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
q[2] = Qb.ReadBits(5, 6);
|
||||
C = Qb.ReadBits(0, 4);
|
||||
}
|
||||
|
||||
BitArrayStream Cb = new BitArrayStream(new BitArray(new int[] { C }));
|
||||
if (Cb.ReadBits(0, 2) == 5)
|
||||
{
|
||||
q[1] = 4;
|
||||
q[0] = Cb.ReadBits(3, 4);
|
||||
}
|
||||
else
|
||||
{
|
||||
q[1] = Cb.ReadBits(3, 4);
|
||||
q[0] = Cb.ReadBits(0, 2);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
IntegerEncoded IntEncoded = new IntegerEncoded(EIntegerEncoding.Quint, NumberBitsPerValue)
|
||||
{
|
||||
BitValue = m[i],
|
||||
QuintValue = q[i]
|
||||
};
|
||||
ListIntegerEncoded.Add(IntEncoded);
|
||||
}
|
||||
}
|
||||
|
||||
public static void DecodeIntegerSequence(
|
||||
List<IntegerEncoded> DecodeIntegerSequence,
|
||||
BitArrayStream BitStream,
|
||||
int MaxRange,
|
||||
int NumberValues)
|
||||
{
|
||||
// Determine encoding parameters
|
||||
IntegerEncoded IntEncoded = CreateEncoding(MaxRange);
|
||||
|
||||
// Start decoding
|
||||
int NumberValuesDecoded = 0;
|
||||
while (NumberValuesDecoded < NumberValues)
|
||||
{
|
||||
switch (IntEncoded.GetEncoding())
|
||||
{
|
||||
case EIntegerEncoding.Quint:
|
||||
{
|
||||
DecodeQuintBlock(BitStream, DecodeIntegerSequence, IntEncoded.NumberBits);
|
||||
NumberValuesDecoded += 3;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case EIntegerEncoding.Trit:
|
||||
{
|
||||
DecodeTritBlock(BitStream, DecodeIntegerSequence, IntEncoded.NumberBits);
|
||||
NumberValuesDecoded += 5;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case EIntegerEncoding.JustBits:
|
||||
{
|
||||
IntEncoded.BitValue = BitStream.ReadBits(IntEncoded.NumberBits);
|
||||
DecodeIntegerSequence.Add(IntEncoded);
|
||||
NumberValuesDecoded++;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
117
FModel/PakReader/Textures/BC/BCDecoder.cs
Normal file
117
FModel/PakReader/Textures/BC/BCDecoder.cs
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace PakReader.Textures.BC
|
||||
{
|
||||
public static class BCDecoder
|
||||
{
|
||||
public static byte[] DecodeBC4(byte[] inp, int width, int height)
|
||||
{
|
||||
byte[] ret = new byte[width * height * 4];
|
||||
using var reader = new BinaryReader(new MemoryStream(inp));
|
||||
for (int y_block = 0; y_block < height / 4; y_block++)
|
||||
{
|
||||
for (int x_block = 0; x_block < width / 4; x_block++)
|
||||
{
|
||||
var r_bytes = DecodeBC3Block(reader);
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
ret[GetPixelLoc(width, x_block * 4 + (i % 4), y_block * 4 + (i / 4), 4, 0)] = r_bytes[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
public static byte[] DecodeBC5(byte[] inp, int width, int height)
|
||||
{
|
||||
byte[] ret = new byte[width * height * 4];
|
||||
using var reader = new BinaryReader(new MemoryStream(inp));
|
||||
for (int y_block = 0; y_block < height / 4; y_block++)
|
||||
{
|
||||
for (int x_block = 0; x_block < width / 4; x_block++)
|
||||
{
|
||||
var r_bytes = DecodeBC3Block(reader);
|
||||
var g_bytes = DecodeBC3Block(reader);
|
||||
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
ret[GetPixelLoc(width, x_block * 4 + (i % 4), y_block * 4 + (i / 4), 4, 0)] = r_bytes[i];
|
||||
ret[GetPixelLoc(width, x_block * 4 + (i % 4), y_block * 4 + (i / 4), 4, 1)] = g_bytes[i];
|
||||
ret[GetPixelLoc(width, x_block * 4 + (i % 4), y_block * 4 + (i / 4), 4, 2)] = GetZNormal(r_bytes[i], g_bytes[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int GetPixelLoc(int width, int x, int y, int bpp, int off) => (y * width + x) * bpp + off;
|
||||
|
||||
static byte GetZNormal(byte x, byte y)
|
||||
{
|
||||
var xf = (x / 127.5f) - 1;
|
||||
var yf = (y / 127.5f) - 1;
|
||||
var zval = 1 - xf * xf - yf * yf;
|
||||
var zval_ = (float)Math.Sqrt(zval > 0 ? zval : 0);
|
||||
zval = zval_ < 1 ? zval_ : 1;
|
||||
return (byte)((zval * 127) + 128);
|
||||
}
|
||||
|
||||
static byte[] DecodeBC3Block(BinaryReader reader)
|
||||
{
|
||||
float ref0 = reader.ReadByte();
|
||||
float ref1 = reader.ReadByte();
|
||||
|
||||
float[] ref_sl = new float[8];
|
||||
ref_sl[0] = ref0;
|
||||
ref_sl[1] = ref1;
|
||||
|
||||
if (ref0 > ref1)
|
||||
{
|
||||
ref_sl[2] = (6 * ref0 + 1 * ref1) / 7;
|
||||
ref_sl[3] = (5 * ref0 + 2 * ref1) / 7;
|
||||
ref_sl[4] = (4 * ref0 + 3 * ref1) / 7;
|
||||
ref_sl[5] = (3 * ref0 + 4 * ref1) / 7;
|
||||
ref_sl[6] = (2 * ref0 + 5 * ref1) / 7;
|
||||
ref_sl[7] = (1 * ref0 + 6 * ref1) / 7;
|
||||
}
|
||||
else
|
||||
{
|
||||
ref_sl[2] = (4 * ref0 + 1 * ref1) / 5;
|
||||
ref_sl[3] = (3 * ref0 + 2 * ref1) / 5;
|
||||
ref_sl[4] = (2 * ref0 + 3 * ref1) / 5;
|
||||
ref_sl[5] = (1 * ref0 + 4 * ref1) / 5;
|
||||
ref_sl[6] = 0;
|
||||
ref_sl[7] = 255;
|
||||
}
|
||||
|
||||
byte[] index_block1 = GetBC3Indices(reader.ReadBytes(3));
|
||||
|
||||
byte[] index_block2 = GetBC3Indices(reader.ReadBytes(3));
|
||||
|
||||
byte[] bytes = new byte[16];
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
bytes[7 - i] = (byte)ref_sl[index_block1[i]];
|
||||
}
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
bytes[15 - i] = (byte)ref_sl[index_block2[i]];
|
||||
}
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
static byte[] GetBC3Indices(byte[] buf_block) =>
|
||||
new byte[] {
|
||||
(byte)((buf_block[2] & 0b1110_0000) >> 5),
|
||||
(byte)((buf_block[2] & 0b0001_1100) >> 2),
|
||||
(byte)(((buf_block[2] & 0b0000_0011) << 1) | ((buf_block[1] & 0b1 << 7) >> 7)),
|
||||
(byte)((buf_block[1] & 0b0111_0000) >> 4),
|
||||
(byte)((buf_block[1] & 0b0000_1110) >> 1),
|
||||
(byte)(((buf_block[1] & 0b0000_0001) << 2) | ((buf_block[0] & 0b11 << 6) >> 6)),
|
||||
(byte)((buf_block[0] & 0b0011_1000) >> 3),
|
||||
(byte)(buf_block[0] & 0b0000_0111)
|
||||
};
|
||||
}
|
||||
}
|
||||
263
FModel/PakReader/Textures/DXT/DXTDecoder.cs
Normal file
263
FModel/PakReader/Textures/DXT/DXTDecoder.cs
Normal file
|
|
@ -0,0 +1,263 @@
|
|||
namespace PakReader.Textures.DXT
|
||||
{
|
||||
public static class DXTDecoder
|
||||
{
|
||||
struct Colour8888
|
||||
{
|
||||
public byte red;
|
||||
public byte green;
|
||||
public byte blue;
|
||||
public byte alpha;
|
||||
}
|
||||
|
||||
public static byte[] DecodeDXT1(byte[] inp, int width, int height, int depth)
|
||||
{
|
||||
var bpp = 4;
|
||||
var bps = width * bpp * 1;
|
||||
var sizeofplane = bps * height;
|
||||
|
||||
byte[] rawData = new byte[depth * sizeofplane + height * bps + width * bpp];
|
||||
var colours = new Colour8888[4];
|
||||
colours[0].alpha = 0xFF;
|
||||
colours[1].alpha = 0xFF;
|
||||
colours[2].alpha = 0xFF;
|
||||
|
||||
unsafe
|
||||
{
|
||||
fixed (byte* bytePtr = inp)
|
||||
{
|
||||
byte* temp = bytePtr;
|
||||
for (int z = 0; z < depth; z++)
|
||||
{
|
||||
for (int y = 0; y < height; y += 4)
|
||||
{
|
||||
for (int x = 0; x < width; x += 4)
|
||||
{
|
||||
ushort colour0 = *((ushort*)temp);
|
||||
ushort colour1 = *((ushort*)(temp + 2));
|
||||
DxtcReadColor(colour0, ref colours[0]);
|
||||
DxtcReadColor(colour1, ref colours[1]);
|
||||
|
||||
uint bitmask = ((uint*)temp)[1];
|
||||
temp += 8;
|
||||
|
||||
if (colour0 > colour1)
|
||||
{
|
||||
// Four-color block: derive the other two colors.
|
||||
// 00 = color_0, 01 = color_1, 10 = color_2, 11 = color_3
|
||||
// These 2-bit codes correspond to the 2-bit fields
|
||||
// stored in the 64-bit block.
|
||||
colours[2].blue = (byte)((2 * colours[0].blue + colours[1].blue + 1) / 3);
|
||||
colours[2].green = (byte)((2 * colours[0].green + colours[1].green + 1) / 3);
|
||||
colours[2].red = (byte)((2 * colours[0].red + colours[1].red + 1) / 3);
|
||||
//colours[2].alpha = 0xFF;
|
||||
|
||||
colours[3].blue = (byte)((colours[0].blue + 2 * colours[1].blue + 1) / 3);
|
||||
colours[3].green = (byte)((colours[0].green + 2 * colours[1].green + 1) / 3);
|
||||
colours[3].red = (byte)((colours[0].red + 2 * colours[1].red + 1) / 3);
|
||||
colours[3].alpha = 0xFF;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Three-color block: derive the other color.
|
||||
// 00 = color_0, 01 = color_1, 10 = color_2,
|
||||
// 11 = transparent.
|
||||
// These 2-bit codes correspond to the 2-bit fields
|
||||
// stored in the 64-bit block.
|
||||
colours[2].blue = (byte)((colours[0].blue + colours[1].blue) / 2);
|
||||
colours[2].green = (byte)((colours[0].green + colours[1].green) / 2);
|
||||
colours[2].red = (byte)((colours[0].red + colours[1].red) / 2);
|
||||
//colours[2].alpha = 0xFF;
|
||||
|
||||
colours[3].blue = (byte)((colours[0].blue + 2 * colours[1].blue + 1) / 3);
|
||||
colours[3].green = (byte)((colours[0].green + 2 * colours[1].green + 1) / 3);
|
||||
colours[3].red = (byte)((colours[0].red + 2 * colours[1].red + 1) / 3);
|
||||
colours[3].alpha = 0x00;
|
||||
}
|
||||
|
||||
for (int j = 0, k = 0; j < 4; j++)
|
||||
{
|
||||
for (int i = 0; i < 4; i++, k++)
|
||||
{
|
||||
int select = (int)((bitmask & (0x03 << k * 2)) >> k * 2);
|
||||
Colour8888 col = colours[select];
|
||||
if (((x + i) < width) && ((y + j) < height))
|
||||
{
|
||||
uint offset = (uint)(z * sizeofplane + (y + j) * bps + (x + i) * bpp);
|
||||
rawData[offset + 0] = col.red;
|
||||
rawData[offset + 1] = col.green;
|
||||
rawData[offset + 2] = col.blue;
|
||||
rawData[offset + 3] = col.alpha;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return rawData;
|
||||
}
|
||||
|
||||
public static byte[] DecodeDXT5(byte[] inp, int width, int height, int depth)
|
||||
{
|
||||
var bpp = 4;
|
||||
var bps = width * bpp * 1;
|
||||
var sizeofplane = bps * height;
|
||||
|
||||
byte[] rawData = new byte[depth * sizeofplane + height * bps + width * bpp];
|
||||
var colours = new Colour8888[4];
|
||||
ushort[] alphas = new ushort[8];
|
||||
|
||||
unsafe
|
||||
{
|
||||
fixed (byte* bytePtr = inp)
|
||||
{
|
||||
byte* temp = bytePtr;
|
||||
for (int z = 0; z < depth; z++)
|
||||
{
|
||||
for (int y = 0; y < height; y += 4)
|
||||
{
|
||||
for (int x = 0; x < width; x += 4)
|
||||
{
|
||||
if (y >= height || x >= width)
|
||||
break;
|
||||
|
||||
alphas[0] = temp[0];
|
||||
alphas[1] = temp[1];
|
||||
byte* alphamask = (temp + 2);
|
||||
temp += 8;
|
||||
|
||||
DxtcReadColors(temp, colours);
|
||||
uint bitmask = ((uint*)temp)[1];
|
||||
temp += 8;
|
||||
|
||||
// Four-color block: derive the other two colors.
|
||||
// 00 = color_0, 01 = color_1, 10 = color_2, 11 = color_3
|
||||
// These 2-bit codes correspond to the 2-bit fields
|
||||
// stored in the 64-bit block.
|
||||
colours[2].blue = (byte)((2 * colours[0].blue + colours[1].blue + 1) / 3);
|
||||
colours[2].green = (byte)((2 * colours[0].green + colours[1].green + 1) / 3);
|
||||
colours[2].red = (byte)((2 * colours[0].red + colours[1].red + 1) / 3);
|
||||
//colours[2].alpha = 0xFF;
|
||||
|
||||
colours[3].blue = (byte)((colours[0].blue + 2 * colours[1].blue + 1) / 3);
|
||||
colours[3].green = (byte)((colours[0].green + 2 * colours[1].green + 1) / 3);
|
||||
colours[3].red = (byte)((colours[0].red + 2 * colours[1].red + 1) / 3);
|
||||
//colours[3].alpha = 0xFF;
|
||||
|
||||
int k = 0;
|
||||
for (int j = 0; j < 4; j++)
|
||||
{
|
||||
for (int i = 0; i < 4; k++, i++)
|
||||
{
|
||||
int select = (int)((bitmask & (0x03 << k * 2)) >> k * 2);
|
||||
Colour8888 col = colours[select];
|
||||
// only put pixels out < width or height
|
||||
if (((x + i) < width) && ((y + j) < height))
|
||||
{
|
||||
uint offset = (uint)(z * sizeofplane + (y + j) * bps + (x + i) * bpp);
|
||||
rawData[offset] = col.red;
|
||||
rawData[offset + 1] = col.green;
|
||||
rawData[offset + 2] = col.blue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 8-alpha or 6-alpha block?
|
||||
if (alphas[0] > alphas[1])
|
||||
{
|
||||
// 8-alpha block: derive the other six alphas.
|
||||
// Bit code 000 = alpha_0, 001 = alpha_1, others are interpolated.
|
||||
alphas[2] = (ushort)((6 * alphas[0] + 1 * alphas[1] + 3) / 7); // bit code 010
|
||||
alphas[3] = (ushort)((5 * alphas[0] + 2 * alphas[1] + 3) / 7); // bit code 011
|
||||
alphas[4] = (ushort)((4 * alphas[0] + 3 * alphas[1] + 3) / 7); // bit code 100
|
||||
alphas[5] = (ushort)((3 * alphas[0] + 4 * alphas[1] + 3) / 7); // bit code 101
|
||||
alphas[6] = (ushort)((2 * alphas[0] + 5 * alphas[1] + 3) / 7); // bit code 110
|
||||
alphas[7] = (ushort)((1 * alphas[0] + 6 * alphas[1] + 3) / 7); // bit code 111
|
||||
}
|
||||
else
|
||||
{
|
||||
// 6-alpha block.
|
||||
// Bit code 000 = alpha_0, 001 = alpha_1, others are interpolated.
|
||||
alphas[2] = (ushort)((4 * alphas[0] + 1 * alphas[1] + 2) / 5); // Bit code 010
|
||||
alphas[3] = (ushort)((3 * alphas[0] + 2 * alphas[1] + 2) / 5); // Bit code 011
|
||||
alphas[4] = (ushort)((2 * alphas[0] + 3 * alphas[1] + 2) / 5); // Bit code 100
|
||||
alphas[5] = (ushort)((1 * alphas[0] + 4 * alphas[1] + 2) / 5); // Bit code 101
|
||||
alphas[6] = 0x00; // Bit code 110
|
||||
alphas[7] = 0xFF; // Bit code 111
|
||||
}
|
||||
|
||||
// Note: Have to separate the next two loops,
|
||||
// it operates on a 6-byte system.
|
||||
|
||||
// First three bytes
|
||||
//uint bits = (uint)(alphamask[0]);
|
||||
uint bits = (uint)((alphamask[0]) | (alphamask[1] << 8) | (alphamask[2] << 16));
|
||||
for (int j = 0; j < 2; j++)
|
||||
{
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
// only put pixels out < width or height
|
||||
if (((x + i) < width) && ((y + j) < height))
|
||||
{
|
||||
uint offset = (uint)(z * sizeofplane + (y + j) * bps + (x + i) * bpp + 3);
|
||||
rawData[offset] = (byte)alphas[bits & 0x07];
|
||||
}
|
||||
bits >>= 3;
|
||||
}
|
||||
}
|
||||
|
||||
// Last three bytes
|
||||
//bits = (uint)(alphamask[3]);
|
||||
bits = (uint)((alphamask[3]) | (alphamask[4] << 8) | (alphamask[5] << 16));
|
||||
for (int j = 2; j < 4; j++)
|
||||
{
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
// only put pixels out < width or height
|
||||
if (((x + i) < width) && ((y + j) < height))
|
||||
{
|
||||
uint offset = (uint)(z * sizeofplane + (y + j) * bps + (x + i) * bpp + 3);
|
||||
rawData[offset] = (byte)alphas[bits & 0x07];
|
||||
}
|
||||
bits >>= 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return rawData;
|
||||
}
|
||||
}
|
||||
|
||||
static unsafe void DxtcReadColors(byte* data, Colour8888[] op)
|
||||
{
|
||||
byte buf = (byte)((data[1] & 0xF8) >> 3);
|
||||
op[0].red = (byte)(buf << 3 | buf >> 2);
|
||||
buf = (byte)(((data[0] & 0xE0) >> 5) | ((data[1] & 0x7) << 3));
|
||||
op[0].green = (byte)(buf << 2 | buf >> 3);
|
||||
buf = (byte)(data[0] & 0x1F);
|
||||
op[0].blue = (byte)(buf << 3 | buf >> 2);
|
||||
|
||||
buf = (byte)((data[3] & 0xF8) >> 3);
|
||||
op[1].red = (byte)(buf << 3 | buf >> 2);
|
||||
buf = (byte)(((data[2] & 0xE0) >> 5) | ((data[3] & 0x7) << 3));
|
||||
op[1].green = (byte)(buf << 2 | buf >> 3);
|
||||
buf = (byte)(data[2] & 0x1F);
|
||||
op[1].blue = (byte)(buf << 3 | buf >> 2);
|
||||
}
|
||||
|
||||
static void DxtcReadColor(ushort data, ref Colour8888 op)
|
||||
{
|
||||
byte buf = (byte)((data & 0xF800) >> 11);
|
||||
op.red = (byte)(buf << 3 | buf >> 2);
|
||||
buf = (byte)((data & 0x7E0) >> 5);
|
||||
op.green = (byte)(buf << 2 | buf >> 3);
|
||||
buf = (byte)(data & 0x1f);
|
||||
op.blue = (byte)(buf << 3 | buf >> 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
68
FModel/PakReader/Textures/TextureDecoder.cs
Normal file
68
FModel/PakReader/Textures/TextureDecoder.cs
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
using System;
|
||||
using PakReader.Parsers.Objects;
|
||||
using PakReader.Textures.ASTC;
|
||||
using PakReader.Textures.BC;
|
||||
using PakReader.Textures.DXT;
|
||||
using SkiaSharp;
|
||||
|
||||
namespace PakReader.Textures
|
||||
{
|
||||
static class TextureDecoder
|
||||
{
|
||||
public static SKImage DecodeImage(byte[] sequence, int width, int height, int depth, EPixelFormat format)
|
||||
{
|
||||
byte[] data;
|
||||
SKColorType colorType;
|
||||
switch (format)
|
||||
{
|
||||
case EPixelFormat.PF_DXT5:
|
||||
data = DXTDecoder.DecodeDXT5(sequence, width, height, depth);
|
||||
colorType = SKColorType.Rgba8888;
|
||||
break;
|
||||
case EPixelFormat.PF_DXT1:
|
||||
data = DXTDecoder.DecodeDXT1(sequence, width, height, depth);
|
||||
colorType = SKColorType.Rgba8888;
|
||||
break;
|
||||
case EPixelFormat.PF_ASTC_8x8:
|
||||
var x = (int)TextureFormatHelper.GetBlockWidth(format);
|
||||
var y = (int)TextureFormatHelper.GetBlockHeight(format);
|
||||
var z = (int)TextureFormatHelper.GetBlockDepth(format);
|
||||
data = ASTCDecoder.DecodeToRGBA8888(sequence, x, y, z, width, height, 1);
|
||||
colorType = SKColorType.Rgba8888;
|
||||
break;
|
||||
case EPixelFormat.PF_B8G8R8A8:
|
||||
data = sequence;
|
||||
colorType = SKColorType.Bgra8888;
|
||||
break;
|
||||
case EPixelFormat.PF_BC5:
|
||||
data = BCDecoder.DecodeBC5(sequence, width, height);
|
||||
colorType = SKColorType.Bgra8888;
|
||||
break;
|
||||
case EPixelFormat.PF_BC4:
|
||||
data = BCDecoder.DecodeBC4(sequence, width, height);
|
||||
colorType = SKColorType.Bgra8888;
|
||||
break;
|
||||
case EPixelFormat.PF_G8:
|
||||
data = sequence;
|
||||
colorType = SKColorType.Gray8;
|
||||
break;
|
||||
case EPixelFormat.PF_FloatRGBA:
|
||||
data = sequence;
|
||||
colorType = SKColorType.RgbaF16;
|
||||
break;
|
||||
default:
|
||||
throw new NotImplementedException($"Cannot decode {format} format");
|
||||
}
|
||||
|
||||
using var bitmap = new SKBitmap(new SKImageInfo(width, height, colorType, SKAlphaType.Unpremul));
|
||||
unsafe
|
||||
{
|
||||
fixed (byte* p = data)
|
||||
{
|
||||
bitmap.SetPixels(new IntPtr(p));
|
||||
}
|
||||
}
|
||||
return SKImage.FromBitmap(bitmap);
|
||||
}
|
||||
}
|
||||
}
|
||||
159
FModel/PakReader/Textures/TextureFormatHelper.cs
Normal file
159
FModel/PakReader/Textures/TextureFormatHelper.cs
Normal file
|
|
@ -0,0 +1,159 @@
|
|||
using PakReader.Parsers.Objects;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace PakReader.Textures
|
||||
{
|
||||
public class TextureFormatHelper
|
||||
{
|
||||
private enum TargetBuffer
|
||||
{
|
||||
Color = 1,
|
||||
Depth = 2,
|
||||
Stencil = 3,
|
||||
DepthStencil = 4,
|
||||
}
|
||||
|
||||
public static uint GetBlockWidth(EPixelFormat Format)
|
||||
{
|
||||
if (!HasFormatTableKey(Format)) return 8;
|
||||
return FormatTable[GetBaseFormat(Format)].BlockWidth;
|
||||
}
|
||||
|
||||
public static uint GetBlockHeight(EPixelFormat Format)
|
||||
{
|
||||
if (!HasFormatTableKey(Format)) return 8;
|
||||
return FormatTable[GetBaseFormat(Format)].BlockHeight;
|
||||
}
|
||||
|
||||
public static uint GetBytesPerPixel(EPixelFormat Format)
|
||||
{
|
||||
if (!HasFormatTableKey(Format)) return 8;
|
||||
return FormatTable[GetBaseFormat(Format)].BytesPerPixel;
|
||||
}
|
||||
|
||||
public static uint GetBlockDepth(EPixelFormat Format)
|
||||
{
|
||||
if (!HasFormatTableKey(Format)) return 8;
|
||||
return FormatTable[GetBaseFormat(Format)].BlockDepth;
|
||||
}
|
||||
|
||||
public static bool IsBCNCompressed(EPixelFormat Format)
|
||||
{
|
||||
return Format.ToString().StartsWith("BC");
|
||||
}
|
||||
|
||||
public static bool HasFormatTableKey(EPixelFormat Format)
|
||||
{
|
||||
return FormatTable.ContainsKey(GetBaseFormat(Format));
|
||||
}
|
||||
|
||||
private static string GetBaseFormat(EPixelFormat format)
|
||||
{
|
||||
string[] items = { "_UNORM", "_SRGB", "_SINT", "_SNORM", "_UINT", "_UFLOAT", "_SFLOAT", "_FLOAT", "H_SF16", "H_UF16" };
|
||||
string output = format.ToString();
|
||||
for (int i = 0; i < items.Length; i++)
|
||||
output = output.Replace(items[i], string.Empty);
|
||||
return output;
|
||||
}
|
||||
|
||||
private static readonly Dictionary<string, FormatInfo> FormatTable =
|
||||
new Dictionary<string, FormatInfo>()
|
||||
{
|
||||
{ "RGBA32", new FormatInfo(16, 1, 1, 1, TargetBuffer.Color) },
|
||||
{ "RGBA16", new FormatInfo(8, 1, 1, 1, TargetBuffer.Color) },
|
||||
{ "RGBA8", new FormatInfo(4, 1, 1, 1, TargetBuffer.Color) },
|
||||
{ "RGB8", new FormatInfo(3, 1, 1, 1, TargetBuffer.Color) },
|
||||
{ "RGBA4", new FormatInfo(2, 1, 1, 1, TargetBuffer.Color) },
|
||||
{ "RGB32", new FormatInfo(8, 1, 1, 1, TargetBuffer.Color) },
|
||||
{ "RG32", new FormatInfo(8, 1, 1, 1, TargetBuffer.Color) },
|
||||
{ "RG16", new FormatInfo(4, 1, 1, 1, TargetBuffer.Color) },
|
||||
{ "RG8", new FormatInfo(2, 1, 1, 1, TargetBuffer.Color) },
|
||||
{ "RG4", new FormatInfo(1, 1, 1, 1, TargetBuffer.Color) },
|
||||
{ "R32", new FormatInfo(4, 1, 1, 1, TargetBuffer.Color) },
|
||||
{ "R16", new FormatInfo(2, 1, 1, 1, TargetBuffer.Color) },
|
||||
{ "R8", new FormatInfo(1, 1, 1, 1, TargetBuffer.Color) },
|
||||
|
||||
{ "R32G8X24", new FormatInfo(8, 1, 1, 1, TargetBuffer.Color) },
|
||||
{ "RGBG8", new FormatInfo(4, 1, 1, 1, TargetBuffer.Color) },
|
||||
{ "BGRX8", new FormatInfo(4, 1, 1, 1, TargetBuffer.Color) },
|
||||
{ "BGR5A1", new FormatInfo(2, 1, 1, 1, TargetBuffer.Color) },
|
||||
{ "RGB5A1", new FormatInfo(2, 1, 1, 1, TargetBuffer.Color) },
|
||||
{ "BGRA8", new FormatInfo(4, 1, 1, 1, TargetBuffer.Color) },
|
||||
|
||||
{ "R5G5B5", new FormatInfo(2, 1, 1, 1, TargetBuffer.Color) },
|
||||
{ "RGBB10A2", new FormatInfo(4, 1, 1, 1, TargetBuffer.Color) },
|
||||
{ "RG11B10", new FormatInfo(4, 1, 1, 1, TargetBuffer.Color) },
|
||||
|
||||
{ "BGRA4", new FormatInfo(2, 1, 1, 1, TargetBuffer.Color) },
|
||||
{ "B5G6R5", new FormatInfo(2, 1, 1, 1, TargetBuffer.Color) },
|
||||
|
||||
{ "BC1", new FormatInfo(8, 4, 4, 1, TargetBuffer.Color) },
|
||||
{ "BC2", new FormatInfo(16, 4, 4, 1, TargetBuffer.Color) },
|
||||
{ "BC3", new FormatInfo(16, 4, 4, 1, TargetBuffer.Color) },
|
||||
{ "BC4", new FormatInfo(8, 4, 4, 1, TargetBuffer.Color) },
|
||||
{ "BC5", new FormatInfo(16, 4, 4, 1, TargetBuffer.Color) },
|
||||
{ "BC6", new FormatInfo(16, 4, 4, 1, TargetBuffer.Color) },
|
||||
{ "BC7", new FormatInfo(16, 4, 4, 1, TargetBuffer.Color) },
|
||||
|
||||
{ "ASTC_4x4", new FormatInfo(16, 4, 4, 1, TargetBuffer.Color) },
|
||||
{ "ASTC_5x4", new FormatInfo(16, 5, 4, 1, TargetBuffer.Color) },
|
||||
{ "ASTC_5x5", new FormatInfo(16, 5, 5, 1, TargetBuffer.Color) },
|
||||
{ "ASTC_6x5", new FormatInfo(16, 6, 5, 1, TargetBuffer.Color) },
|
||||
{ "ASTC_6x6", new FormatInfo(16, 6, 6, 1, TargetBuffer.Color) },
|
||||
{ "ASTC_8x5", new FormatInfo(16, 8, 5, 1, TargetBuffer.Color) },
|
||||
{ "ASTC_8x6", new FormatInfo(16, 8, 6, 1, TargetBuffer.Color) },
|
||||
{ "ASTC_8x8", new FormatInfo(16, 8, 8, 1, TargetBuffer.Color) },
|
||||
{ "ASTC_10x5", new FormatInfo(16, 10, 5, 1, TargetBuffer.Color) },
|
||||
{ "ASTC_10x6", new FormatInfo(16, 10, 6, 1, TargetBuffer.Color) },
|
||||
{ "ASTC_10x8", new FormatInfo(16, 10, 8, 1, TargetBuffer.Color) },
|
||||
{ "ASTC_10x10", new FormatInfo(16, 10, 10, 1, TargetBuffer.Color) },
|
||||
{ "ASTC_12x10", new FormatInfo(16, 12, 10, 1, TargetBuffer.Color) },
|
||||
{ "ASTC_12x12", new FormatInfo(16, 12, 12, 1, TargetBuffer.Color) },
|
||||
|
||||
{ "ETC1", new FormatInfo(4, 1, 1, 1, TargetBuffer.Color) },
|
||||
{ "ETC1_A4", new FormatInfo(8, 1, 1, 1, TargetBuffer.Color) },
|
||||
{ "HIL08", new FormatInfo(16, 1, 1, 1, TargetBuffer.Color) },
|
||||
{ "L4", new FormatInfo(4, 1, 1, 1, TargetBuffer.Color) },
|
||||
{ "LA4", new FormatInfo(4, 1, 1, 1, TargetBuffer.Color) },
|
||||
{ "L8", new FormatInfo(8, 1, 1, 1, TargetBuffer.Color) },
|
||||
{ "LA8", new FormatInfo(16, 1, 1, 1, TargetBuffer.Color) },
|
||||
{ "A4", new FormatInfo(4, 1, 1, 1, TargetBuffer.Color) },
|
||||
{ "A8", new FormatInfo(8, 1, 1, 1, TargetBuffer.Color) },
|
||||
|
||||
{ "D16", new FormatInfo(2, 1, 1, 1, TargetBuffer.Depth) },
|
||||
{ "D24S8", new FormatInfo(4, 1, 1, 1, TargetBuffer.Depth) },
|
||||
{ "D32", new FormatInfo(4, 1, 1, 1, TargetBuffer.Depth) },
|
||||
{ "D24_UNORM_S8", new FormatInfo(8, 1, 1, 1, TargetBuffer.Depth) },
|
||||
{ "D32_FLOAT_S8X24", new FormatInfo(8, 1, 1, 1, TargetBuffer.Depth) },
|
||||
{ "I4", new FormatInfo(4, 8, 8, 1, TargetBuffer.Color) },
|
||||
{ "I8", new FormatInfo(8, 8, 4, 1, TargetBuffer.Color) },
|
||||
{ "IA4", new FormatInfo(8, 8, 4, 1, TargetBuffer.Color) },
|
||||
{ "IA8", new FormatInfo(16, 4, 4, 1, TargetBuffer.Color) },
|
||||
{ "RGB565", new FormatInfo(2, 4, 4, 1, TargetBuffer.Color) },
|
||||
{ "RGB5A3", new FormatInfo(16, 4, 4, 1, TargetBuffer.Color) },
|
||||
{ "C4", new FormatInfo(4, 8, 8, 1, TargetBuffer.Color) },
|
||||
{ "C8", new FormatInfo(8, 8, 4, 1, TargetBuffer.Color) },
|
||||
{ "C14X2", new FormatInfo(16, 4, 4, 1, TargetBuffer.Color) },
|
||||
{ "CMPR", new FormatInfo(4, 8, 8, 1, TargetBuffer.Color) }
|
||||
};
|
||||
|
||||
class FormatInfo
|
||||
{
|
||||
public uint BytesPerPixel { get; private set; }
|
||||
public uint BlockWidth { get; private set; }
|
||||
public uint BlockHeight { get; private set; }
|
||||
public uint BlockDepth { get; private set; }
|
||||
|
||||
public TargetBuffer TargetBuffer;
|
||||
|
||||
public FormatInfo(uint bytesPerPixel, uint blockWidth, uint blockHeight, uint blockDepth, TargetBuffer targetBuffer)
|
||||
{
|
||||
BytesPerPixel = bytesPerPixel;
|
||||
BlockWidth = blockWidth;
|
||||
BlockHeight = blockHeight;
|
||||
BlockDepth = blockDepth;
|
||||
TargetBuffer = targetBuffer;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user