mirror of
https://github.com/Gericom/EveryFileExplorer.git
synced 2025-06-19 01:15:36 -04:00
506 lines
23 KiB
C#
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;
|
|
}
|
|
|
|
}
|
|
}
|