EveryFileExplorer/WiiU/GPU/Textures.cs
Gericom f9f00d193c Importing textures for bcmdl is possible now
And some older stuff I didn't commit yet.
2015-11-03 16:18:38 +01:00

506 lines
23 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
using LibEveryFileExplorer.IO;
using LibEveryFileExplorer.GFX;
namespace WiiU.GPU
{
public class Textures
{
public enum TileMode : uint
{
Default = 0,
LinearAligned = 1,
Tiled1DThin1 = 2,
Tiled1DThick = 3,
Tiled2DThin1 = 4,
Tiled2DThin2 = 5,
Tiled2DThin4 = 6,
Tiled2DThick = 7,
Tiled2BThin1 = 8,
Tiled2BThin2 = 9,
Tiled2BThin4 = 10,
Tiled2BThick = 11,
Tiled3DThin1 = 12,
Tiled3DThick = 13,
Tiled3BThin1 = 14,
Tiled3BThick = 15,
LinearSpecial = 16
}
public enum ImageFormat : uint
{
RGB565,
DXT5
//ETC1,
//ETC1A4
}
//private static readonly int[] Bpp = { 32, 24, 16, 16, 16, 16, 16, 8, 8, 8, 4, 4, 4, 8 };
private static readonly int[] TileOrder =
{
0, 1, 4, 5,
2, 3, 6, 7,
8, 9, 12, 13,
10, 11, 14, 15
};
//1. Swap column 1 and 2, 4 and 5 etc.
//2. Put back 8x8 tiles in 16x16 blocks in the order 0, 3
// 1, 2
private static readonly int[,] ETC1Modifiers =
{
{ 2, 8 },
{ 5, 17 },
{ 9, 29 },
{ 13, 42 },
{ 18, 60 },
{ 24, 80 },
{ 33, 106 },
{ 47, 183 }
};
//public static int GetBpp(ImageFormat Format) { return Bpp[(uint)Format]; }
public static Bitmap ToBitmap(byte[] Data, int Width, int Height, ImageFormat Format, TileMode TileMode, uint SwizzleMode, bool ExactSize = false)
{
return ToBitmap(Data, 0, Width, Height, Format, TileMode, SwizzleMode, ExactSize);
}
public static unsafe Bitmap ToBitmap(byte[] Data, int Offset, int Width, int Height, ImageFormat Format, TileMode TileMode, uint SwizzleMode, bool ExactSize = false)
{
if (Data == null || Data.Length < 1 || Offset < 0 || Offset >= Data.Length || Width < 1 || Height < 1) return null;
if (ExactSize && ((Width % 8) != 0 || (Height % 8) != 0)) return null;
int physicalwidth = Width;
int physicalheight = Height;
if (!ExactSize)
{
Width = 1 << (int)Math.Ceiling(Math.Log(Width, 2));
Height = 1 << (int)Math.Ceiling(Math.Log(Height, 2));
}
Bitmap bitm = new Bitmap(Width, Height);//physicalwidth, physicalheight);
BitmapData d = bitm.LockBits(new Rectangle(0, 0, bitm.Width, bitm.Height), ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);
uint* res = (uint*)d.Scan0;
int offs = Offset;//0;
int stride = d.Stride / 4;
switch (Format)
{
case ImageFormat.RGB565:
for (int y = 0; y < Height; y++)
{
for (int x = 0; x < Width; x++)
{
//if (x >= physicalwidth) continue;
if (y >= physicalheight) continue;
res[y * stride + x] =
GFXUtil.ConvertColorFormat(
IOUtil.ReadU16LE(Data, offs),
ColorFormat.RGB565,
ColorFormat.ARGB8888);
offs += 2;
}
}
break;
case ImageFormat.DXT5:
for (int y2 = 0; y2 < Height; y2 += 4)
{
for (int x2 = 0; x2 < Width; x2 += 4)
{
ulong a_data = IOUtil.ReadU64LE(Data, offs);
byte[] AlphaPalette = new byte[8];
AlphaPalette[0] = (byte)(a_data & 0xFF);
AlphaPalette[1] = (byte)((a_data >> 8) & 0xFF);
a_data >>= 16;
if (AlphaPalette[0] > AlphaPalette[1])
{
AlphaPalette[2] = (byte)((6 * AlphaPalette[0] + 1 * AlphaPalette[1]) / 7);
AlphaPalette[3] = (byte)((5 * AlphaPalette[0] + 2 * AlphaPalette[1]) / 7);
AlphaPalette[4] = (byte)((4 * AlphaPalette[0] + 3 * AlphaPalette[1]) / 7);
AlphaPalette[5] = (byte)((3 * AlphaPalette[0] + 4 * AlphaPalette[1]) / 7);
AlphaPalette[6] = (byte)((2 * AlphaPalette[0] + 5 * AlphaPalette[1]) / 7);
AlphaPalette[7] = (byte)((1 * AlphaPalette[0] + 6 * AlphaPalette[1]) / 7);
}
else
{
AlphaPalette[2] = (byte)((4 * AlphaPalette[0] + 1 * AlphaPalette[1]) / 5);
AlphaPalette[3] = (byte)((3 * AlphaPalette[0] + 2 * AlphaPalette[1]) / 5);
AlphaPalette[4] = (byte)((2 * AlphaPalette[0] + 3 * AlphaPalette[1]) / 5);
AlphaPalette[5] = (byte)((1 * AlphaPalette[0] + 4 * AlphaPalette[1]) / 5);
AlphaPalette[6] = 0;
AlphaPalette[7] = 255;
}
offs += 8;
ushort color0 = IOUtil.ReadU16LE(Data, offs);
ushort color1 = IOUtil.ReadU16LE(Data, offs + 2);
uint data = IOUtil.ReadU32LE(Data, offs + 4);
uint[] Palette = new uint[4];
Palette[0] = GFXUtil.ConvertColorFormat(color0, ColorFormat.RGB565, ColorFormat.ARGB8888);
Palette[1] = GFXUtil.ConvertColorFormat(color1, ColorFormat.RGB565, ColorFormat.ARGB8888);
Color a = System.Drawing.Color.FromArgb((int)Palette[0]);
Color b = System.Drawing.Color.FromArgb((int)Palette[1]);
if (color0 > color1)//1/3 and 2/3
{
Palette[2] = GFXUtil.ToColorFormat((a.R * 2 + b.R * 1) / 3, (a.G * 2 + b.G * 1) / 3, (a.B * 2 + b.B * 1) / 3, ColorFormat.ARGB8888);
Palette[3] = GFXUtil.ToColorFormat((a.R * 1 + b.R * 2) / 3, (a.G * 1 + b.G * 2) / 3, (a.B * 1 + b.B * 2) / 3, ColorFormat.ARGB8888);
}
else//1/2 and transparent
{
Palette[2] = GFXUtil.ToColorFormat((a.R + b.R) / 2, (a.G + b.G) / 2, (a.B + b.B) / 2, ColorFormat.ARGB8888);
Palette[3] = 0;
}
int q = 30;
int aq = 45;
for (int y3 = 0; y3 < 4; y3++)
{
for (int x3 = 0; x3 < 4; x3++)
{
//if (x2 + x3 >= physicalwidth) continue;
if (y2 + y3 >= physicalheight) continue;
res[(y2 + y3) * stride + x2 + x3] = (Palette[(data >> q) & 3] & 0xFFFFFF) | ((uint)AlphaPalette[(a_data >> aq) & 7] << 24);
q -= 2;
aq -= 3;
}
}
offs += 8;
}
// }
//}
}
break;
/*case ImageFormat.ETC1://Some reference: http://www.khronos.org/registry/gles/extensions/OES/OES_compressed_ETC1_RGB8_texture.txt
case ImageFormat.ETC1A4:
{
for (int y = 0; y < Height; y += 8)
{
for (int x = 0; x < Width; x += 8)
{
for (int i = 0; i < 8; i += 4)
{
for (int j = 0; j < 8; j += 4)
{
ulong alpha = 0xFFFFFFFFFFFFFFFF;
ulong data = IOUtil.ReadU64BE(Data, offs);
if (Format == ImageFormat.ETC1A4)
{
offs += 8;
alpha = IOUtil.ReadU64BE(Data, offs);
}
bool diffbit = ((data >> 33) & 1) == 1;
bool flipbit = ((data >> 32) & 1) == 1; //0: |||, 1: |-|
int r1, r2, g1, g2, b1, b2;
if (diffbit) //'differential' mode
{
int r = (int)((data >> 59) & 0x1F);
int g = (int)((data >> 51) & 0x1F);
int b = (int)((data >> 43) & 0x1F);
r1 = (r << 3) | ((r & 0x1C) >> 2);
g1 = (g << 3) | ((g & 0x1C) >> 2);
b1 = (b << 3) | ((b & 0x1C) >> 2);
r += (int)((data >> 56) & 0x7) << 29 >> 29;
g += (int)((data >> 48) & 0x7) << 29 >> 29;
b += (int)((data >> 40) & 0x7) << 29 >> 29;
r2 = (r << 3) | ((r & 0x1C) >> 2);
g2 = (g << 3) | ((g & 0x1C) >> 2);
b2 = (b << 3) | ((b & 0x1C) >> 2);
}
else //'individual' mode
{
r1 = (int)((data >> 60) & 0xF) * 0x11;
g1 = (int)((data >> 52) & 0xF) * 0x11;
b1 = (int)((data >> 44) & 0xF) * 0x11;
r2 = (int)((data >> 56) & 0xF) * 0x11;
g2 = (int)((data >> 48) & 0xF) * 0x11;
b2 = (int)((data >> 40) & 0xF) * 0x11;
}
int Table1 = (int)((data >> 37) & 0x7);
int Table2 = (int)((data >> 34) & 0x7);
for (int y3 = 0; y3 < 4; y3++)
{
for (int x3 = 0; x3 < 4; x3++)
{
if (x + j + x3 >= physicalwidth) continue;
if (y + i + y3 >= physicalheight) continue;
int val = (int)((data >> (x3 * 4 + y3)) & 0x1);
bool neg = ((data >> (x3 * 4 + y3 + 16)) & 0x1) == 1;
uint c;
if ((flipbit && y3 < 2) || (!flipbit && x3 < 2))
{
int add = ETC1Modifiers[Table1, val] * (neg ? -1 : 1);
c = GFXUtil.ToColorFormat((byte)(((alpha >> ((x3 * 4 + y3) * 4)) & 0xF) * 0x11), (byte)ColorClamp(r1 + add), (byte)ColorClamp(g1 + add), (byte)ColorClamp(b1 + add), ColorFormat.ARGB8888);
}
else
{
int add = ETC1Modifiers[Table2, val] * (neg ? -1 : 1);
c = GFXUtil.ToColorFormat((byte)(((alpha >> ((x3 * 4 + y3) * 4)) & 0xF) * 0x11), (byte)ColorClamp(r2 + add), (byte)ColorClamp(g2 + add), (byte)ColorClamp(b2 + add), ColorFormat.ARGB8888);
}
res[(i + y3) * stride + x + j + x3] = c;
}
}
offs += 8;
}
}
}
res += stride * 8;
}
}
break;
* */
}
Detile(res, stride, Width, Height, physicalwidth, physicalheight, TileMode);
bitm.UnlockBits(d);
return bitm;
}
private static unsafe void Detile(uint* res, int stride, int width, int height, int physicalwidth, int physicalheight, TileMode Mode)
{
switch (Mode)
{
case Textures.TileMode.Tiled2DThin1:
DetileTiled2DThin1(res, stride, width, height, physicalwidth, physicalheight);
return;
default:
throw new Exception("Unsupported Tilemode!");
}
}
//private static readonly int[] Tiled2DThin1OrderA = { 0, 1, 3, 2 };
//private static readonly int[] Tiled2DThin1OrderB = { 0, 2, 3, 1 };
private static readonly int[] Tiled2DThin1Order = { 2, 0, 1, 3 };
//Micro tiles: 8x8
//Macro tiles: 32x16 (4x2 tiles = 4 banks, 2 pipes)
//Alignment: 2048 pixels (I think), which matches exactly with the 128x16 blocks
//First block:
// Macro:
// 1 2 5 6
// 0 3 4 7
//Second block:
// Macro:
// 5 6 1 2
// 4 7 0 3
//Third block:
// Macro:
// 3 0 7 4
// 2 1 6 5
//Fourth block:
// Macro:
// 7 4 3 0
// 6 5 2 1
private static unsafe void DetileTiled2DThin1(uint* res, int stride, int width, int height, int physicalwidth, int physicalheight)
{
uint[] right_order = new uint[width * height];
int q = 0;
//Let's put the tiles in the right order
for (int y = 0; y < height; y += 8)
{
for (int xx = 0; xx < width; xx += 64)
{
for (int yy = 0; yy < 8; yy++)
{
for (int x = 0; x < 64; x++)
{
right_order[q++] = res[(y + yy) * stride + x + xx];
}
}
}
}
q = 0;
uint[] Result = new uint[width * height];
for (int y = 0; y < height; y += 8)
{
for (int x = 0; x < width; x += 8)
{
for (int yy = 0; yy < 8; yy++)
{
for (int xx = 0; xx < 8; xx++)
{
Result[(y + yy) * width + x + xx] = right_order[q++];
}
}
}
}
//
/* uint[] Result = new uint[width * height];
int px = 0;
int py = 0;
for (int y = 0; y < height; y += /*8/16)
{
for (int x = 0; x < width; x += 64)
{
/*for (int y2 = 0; y2 < 8; y2++)
{
for (int x2 = 0; x2 < 64; x2++)
{
Result[(y + (x2 / 8)) * width + x + y2 * 8 + x2 % 8] =
res[(y + y2) * stride + x + x2];
}
}/
/*for (int i = 0; i < 64; i++)//y2 = 0; y2 < 64; y2 += 8)
{
int tile = i;
int q = Tiled2DThin1Order[(tile / 2) % 4];
int p = tile & ~7;
int xx = (p % 16) * 2 + (q % 2) * 8;
//if ((i / 0x20) == 1)
//{
//xx += (i % 2) * 8;
//}
/*else /
xx += (i % 2) * 32;
int yy = p / 16 * 16 + (q / 2) * 8;
for (int y3 = 0; y3 < 8; y3++)
{
for (int x3 = 0; x3 < 8; x3++)
{
if (y + y3 + yy >= height || x + x3 + xx >= width) continue;
//if (x + x2 + x3 >= physicalwidth) continue;
//if (y + y3 + yy >= physicalheight) continue;
Result[(y + y3 + yy) * width + x + x3 + xx] = ReadPixel(res, stride, width, height, ref px, ref py);
}
}
}/
}
}*/
//TODO: We now have the tiles, so start constructing the macro tiles
/*R600Tiling t = new R600Tiling();
R600Tiling._ADDR_COMPUTE_SURFACE_ADDRFROMCOORD_INPUT r = new R600Tiling._ADDR_COMPUTE_SURFACE_ADDRFROMCOORD_INPUT();
r.bpp = 32;
r.tileMode = 4;
r.tileType = 0;
r.pitch = (uint)stride;
r.height = (uint)height;
r.numSlices = 1;
r.numSamples = (uint)(height * width);
R600Tiling._ADDR_COMPUTE_SURFACE_ADDRFROMCOORD_OUTPUT o = new R600Tiling._ADDR_COMPUTE_SURFACE_ADDRFROMCOORD_OUTPUT();
int i = 0;
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
r.x = (uint)x;
r.y = (uint)y;
r.sample = (uint)i;
int pixel_number = (int)t.ComputeSurfaceAddrFromCoord(ref r, ref o);
/*int pixel_number = 0;
pixel_number |= ((x >> 0) & 1) << 0; // pn[0] = x[0]
pixel_number |= ((x >> 1) & 1) << 1; // pn[1] = x[1]
pixel_number |= ((x >> 2) & 1) << 2; // pn[2] = x[2]
pixel_number |= ((y >> 1) & 1) << 3; // pn[3] = y[1]
pixel_number |= ((y >> 0) & 1) << 4; // pn[4] = y[0]
pixel_number |= ((y >> 2) & 1) << 5; // pn[5] = y[2]/
Result[y * width + x] = res[(pixel_number & 0xFFF) / 4];
i++;
}
}*/
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
//if (x >= physicalwidth) continue;
if (y >= physicalheight) continue;
res[y * stride + x] = Result[y * width + x];
}
}
/*
//Swap columns first!
for (int c = 0; c < width; c += 16)
{
//Swap c + 4 and c + 8
for (int y = 0; y < height; y++)
{
for (int x = 0; x < 4; x++)
{
if (c + x >= physicalwidth) continue;
if (y >= physicalheight) continue;
uint a = res[y * stride + c + x + 4];
uint b = res[y * stride + c + x + 8];
res[y * stride + c + x + 4] = b;
res[y * stride + c + x + 8] = a;
}
}
}
uint[] Result = new uint[width * height];
//work in 16x16 and then in 8x8 tiles
int px = 0;
int py = 0;
for (int y = 0; y < height; y += 32)
{
for (int x = 0; x < width; x += 32)
{
for (int j = 0; j < 4; j++)
{
int x4 = (Tiled2DThin1OrderA[j] % 2) * 16;
int y4 = ((Tiled2DThin1OrderA[j] & 2) >> 1) * 16;
//Read out the 256 pixels needed!
uint[] Pixels = new uint[256];
for (int i = 0; i < 256; i++) Pixels[i] = ReadPixel(res, stride, width, height, ref px, ref py);
int idx = 0;
for (int i = 0; i < 4; i++)
{
int x2 = (Tiled2DThin1OrderB[i] % 2) * 8;
int y2 = ((Tiled2DThin1OrderB[i] & 2) >> 1) * 8;
for (int y3 = 0; y3 < 8; y3++)
{
for (int x3 = 0; x3 < 8; x3++)
{
Result[(y + y2 + y3 + y4) * width + x + x2 + x3 + x4] = Pixels[idx++];
//res[(y + y2 + y3) * stride + x + x2 + x3] = Pixels[idx++];
}
}
}
}
}
}
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
if (x >= physicalwidth) continue;
if (y >= physicalheight) continue;
res[y * stride + x] = Result[y * width + x];
}
}
*/
}
private static unsafe uint ReadPixel(uint* res, int stride, int width, int height, ref int px, ref int py)
{
if (px >= width || py >= height || px < 0 || py < 0)
return 0;//throw new ArgumentException("ReadPixel fail!");
uint result = res[py * stride + px];
px++;
if (px == width)
{
px = 0;
py++;
}
return result;
}
private static int ColorClamp(int Color)
{
if (Color > 255) Color = 255;
if (Color < 0) Color = 0;
return Color;
}
}
}