Files
SM64Lib/SM64Lib.N64Graphics/TextureFormat.cs

599 lines
24 KiB
C#

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace SM64Lib.N64Graphics
{
public class TextureFormats
{
public static Bitmap createColorTexture(Color color)
{
Bitmap tex = new Bitmap(1, 1);
Graphics.FromImage(tex).Clear(color);
return tex;
}
public static string ConvertFormatToString(byte formatColorType, byte formatByteSize)
{
string formatStr = "";
switch (formatColorType & 7)
{
case 0: formatStr = "RGBA"; break;
case 1: formatStr = "YUV"; break;
case 2: formatStr = "CI"; break;
case 3: formatStr = "IA"; break;
case 4: formatStr = "I"; break;
default: formatStr = "UNKNOWN"; break;
}
switch (formatByteSize & 3)
{
case 0: formatStr += "4"; break;
case 1: formatStr += "8"; break;
case 2: formatStr += "16"; break;
case 3: formatStr += "32"; break;
}
return formatStr;
}
public static byte ConvertStringToFormat(string str)
{
str = str.ToLower();
if (str.Equals("rgba16"))
return 0x10;
else if (str.Equals("rgba32"))
return 0x18;
else if (str.Equals("ci4"))
return 0x40;
else if (str.Equals("ci8"))
return 0x48;
else if (str.Equals("ia4"))
return 0x60;
else if (str.Equals("ia8"))
return 0x68;
else if (str.Equals("ia16"))
return 0x70;
else if (str.Equals("i4"))
return 0x80;
else if (str.Equals("i8"))
return 0x88;
else if (str.Equals("1bpp")) // Not a real F3D format.
return 0x00;
return 0x10;
}
public static int getNumberOfBitsForFormat(byte format)
{
switch (format)
{
case 0x00: // Note: "1 bit per pixel" is not a Fast3D format.
return 1;
case 0x40:
case 0x60:
case 0x80:
return 4;
case 0x48:
case 0x68:
case 0x88:
return 8;
case 0x10:
case 0x70:
case 0x90:
default:
return 16;
case 0x18:
return 32;
}
}
public static byte[] encodeTexture(N64Codec format, Bitmap texture)
{
switch (format)
{
default:
case N64Codec.ONEBPP: // Note: "1 bit per pixel" is not a Fast3D format.
return encode1BPP(texture);
case N64Codec.RGBA16:
return encodeRGBA16(texture);
case N64Codec.RGBA32:
return encodeRGBA32(texture);
case N64Codec.IA4:
return encodeIA4(texture);
case N64Codec.IA8:
return encodeIA8(texture);
case N64Codec.IA16:
return encodeIA16(texture);
case N64Codec.I4:
return encodeI4(texture);
case N64Codec.I8:
return encodeI4(texture);
case N64Codec.CI4:
case N64Codec.CI8:
throw new ArgumentException("CI texture encoding is not currently supported in this version.");
}
}
public static byte getBit(int color, int bit)
{
return (byte)(((color >> 24) & 0xFF) > 0 ? (1 << bit) : 0);
}
public static byte[] encode1BPP(Bitmap texture)
{
int data_size = (texture.Width * texture.Height) / 8;
byte[] data = new byte[data_size];
for (int i = 0; i < data_size; i++)
{
int x = (i * 8) % texture.Width;
int y = (i * 8) / texture.Width;
data[i] = (byte)(
getBit(texture.GetPixel(x + 0, y).ToArgb(), 7) |
getBit(texture.GetPixel(x + 1, y).ToArgb(), 6) |
getBit(texture.GetPixel(x + 2, y).ToArgb(), 5) |
getBit(texture.GetPixel(x + 3, y).ToArgb(), 4) |
getBit(texture.GetPixel(x + 4, y).ToArgb(), 3) |
getBit(texture.GetPixel(x + 5, y).ToArgb(), 2) |
getBit(texture.GetPixel(x + 6, y).ToArgb(), 1) |
getBit(texture.GetPixel(x + 7, y).ToArgb(), 0)
);
}
return data;
}
public static byte[] encodeRGBA16(Bitmap texture)
{
int data_size = (texture.Width * texture.Height) * 2;
byte[] data = new byte[data_size];
for (int i = 0; i < data_size / 2; i++)
{
int x = i % texture.Width;
int y = i / texture.Width;
Color pix = texture.GetPixel(x, y);
byte red = (byte)((pix.R / 256.0f) * 32.0f);
byte green = (byte)((pix.G / 256.0f) * 32.0f);
byte blue = (byte)((pix.B / 256.0f) * 32.0f);
byte alpha = (byte)(pix.A >= 128.0f ? 1 : 0);
data[i * 2] = (byte)((red << 3) | (green >> 2));
data[(i * 2) + 1] = (byte)(((green & 3) << 6) | (blue << 1) | alpha);
}
return data;
}
public static byte[] encodeRGBA32(Bitmap texture)
{
int data_size = (texture.Width * texture.Height) * 4;
byte[] data = new byte[data_size];
for (int i = 0; i < data_size / 4; i++)
{
int x = i % texture.Width;
int y = i / texture.Width;
Color pix = texture.GetPixel(x, y);
data[(i * 4) + 0] = pix.R;
data[(i * 4) + 1] = pix.G;
data[(i * 4) + 2] = pix.B;
data[(i * 4) + 3] = pix.A;
}
return data;
}
public static byte[] encodeIA4(Bitmap texture)
{
int data_size = (texture.Width * texture.Height) / 2;
byte[] data = new byte[data_size];
for (int i = 0; i < data_size; i++)
{
int x = (i * 2) % texture.Width;
int y = (i * 2) / texture.Width;
Color pix1 = texture.GetPixel(x, y);
byte pix1_avg = (byte)((((pix1.R + pix1.G + pix1.B) / 3) / 255.0f) * 8.0f);
byte upper = (byte)((pix1_avg << 1) | (pix1.A < 255 ? 0 : 1));
Color pix2 = texture.GetPixel(x + 1, y);
byte pix2_avg = (byte)((((pix2.R + pix2.G + pix2.B) / 3) / 255.0f) * 8.0f);
byte lower = (byte)((pix2_avg << 1) | (pix2.A < 255 ? 0 : 1));
data[i] = (byte)(((upper & 0xF) << 4) | (lower & 0xF));
}
return data;
}
public static byte[] encodeIA8(Bitmap texture)
{
int data_size = texture.Width * texture.Height;
byte[] data = new byte[data_size];
for (int i = 0; i < data_size; i++)
{
int x = i % texture.Width;
int y = i / texture.Width;
Color pix = texture.GetPixel(x, y);
byte pix_avg = (byte)((((pix.R + pix.G + pix.B) / 3) / 255.0f) * 16.0f);
byte pix_alpha = (byte)((pix.A / 255.0f) * 16.0f);
data[i] = (byte)(((pix_avg & 0xF) << 4) | (pix_alpha & 0xF));
}
return data;
}
public static byte[] encodeIA16(Bitmap texture)
{
int data_size = texture.Width * texture.Height * 2;
byte[] data = new byte[data_size];
for (int i = 0; i < data_size / 2; i++)
{
int x = i % texture.Width;
int y = i / texture.Width;
Color pix = texture.GetPixel(x, y);
byte pix_avg = (byte)((pix.R + pix.G + pix.B) / 3);
data[i * 2] = pix_avg;
data[(i * 2) + 1] = pix.A;
}
return data;
}
public static byte[] encodeI4(Bitmap texture)
{
int data_size = (texture.Width * texture.Height) / 2;
byte[] data = new byte[data_size];
for (int i = 0; i < data_size; i++)
{
int x = (i * 2) % texture.Width;
int y = (i * 2) / texture.Width;
Color pix1 = texture.GetPixel(x, y);
byte upper = (byte)((((pix1.R + pix1.G + pix1.B) / 3) / 255.0f) * 16.0f);
Color pix2 = texture.GetPixel(x + 1, y);
byte lower = (byte)((((pix2.R + pix2.G + pix2.B) / 3) / 255.0f) * 16.0f);
data[i] = (byte)(((upper & 0xF) << 4) | (lower & 0xF));
}
return data;
}
public static byte[] encodeI8(Bitmap texture)
{
int data_size = texture.Width * texture.Height;
byte[] data = new byte[data_size];
for (int i = 0; i < data_size; i++)
{
int x = i % texture.Width;
int y = i / texture.Width;
Color pix = texture.GetPixel(x, y);
data[i] = (byte)((pix.R + pix.G + pix.B) / 3);
}
return data;
}
public static Bitmap decodeTexture(N64Codec format, byte[] data, int width, int height, ushort[] palette)
{
switch (format)
{
default:
case N64Codec.ONEBPP: // Note: "1 bit per pixel" is not a Fast3D format.
return decode1BPP(data, width, height);
case N64Codec.RGBA16:
return decodeRGBA16(data, width, height);
case N64Codec.RGBA32:
return decodeRGBA32(data, width, height);
case N64Codec.CI4:
return decodeCI4(data, width, height, palette);
case N64Codec.CI8:
return decodeCI8(data, width, height, palette);
case N64Codec.IA4:
return decodeIA4(data, width, height);
case N64Codec.IA8:
return decodeIA8(data, width, height);
case N64Codec.IA16:
return decodeIA16(data, width, height);
case N64Codec.I4:
return decodeI4(data, width, height);
case N64Codec.I8:
return decodeI8(data, width, height);
}
}
public static Bitmap decode1BPP(byte[] data, int width, int height)
{
Bitmap tex = new Bitmap(width, height);
if (data.Length >= (width * height) / 8) // Sanity Check
{
int len = (width * height) / 8;
for (int i = 0; i < len; ++i)
{
for (int x = 0; x < 8; x++)
{
byte intensity = (byte)((data[i] >> (7 - x)) & 1);
if (intensity > 0)
intensity = 0xFF;
int alpha = intensity;
int pos = (i * 8) + x;
tex.SetPixel(pos % width, pos / width, Color.FromArgb(alpha, intensity, intensity, intensity));
}
}
}
tex.Tag = new string[] { "Format: 1BPP", "Width: " + width,
"Height: " + height };
return tex;
}
public static Bitmap decodeRGBA32(byte[] data, int width, int height)
{
Console.WriteLine("Texture size = (" + width + "x" + height + ")");
Console.WriteLine("data.Length = (" + data.Length + ")");
Bitmap tex = new Bitmap(width, height);
if (data.Length >= width * height * 4) // Sanity Check
{
BitmapData bitmapData = tex.LockBits(new Rectangle(0, 0, width, height),
ImageLockMode.ReadWrite, tex.PixelFormat);
int len = width * height;
for (int i = 0; i < len; i++)
{
// Swap red and blue values
byte temp_red = data[(i * 4) + 0];
data[(i * 4) + 0] = data[(i * 4) + 2];
data[(i * 4) + 2] = temp_red;
}
Marshal.Copy(data, 0, bitmapData.Scan0, data.Length);
tex.UnlockBits(bitmapData);
}
tex.Tag = new string[] { "Format: RGBA32", "Width: " + width,
"Height: " + height };
return tex;
}
public static Bitmap decodeRGBA16(byte[] data, int width, int height)
{
Bitmap tex = new Bitmap(width, height);
if (data.Length >= width * height * 2) // Sanity Check
{
BitmapData bitmapData = tex.LockBits(new Rectangle(0, 0, width, height),
ImageLockMode.ReadWrite, tex.PixelFormat);
byte[] pixels = new byte[width * height * 4];
int len = width * height;
for (int i = 0; i < len; i++)
{
ushort pixel = (ushort)((data[i * 2] << 8) | data[i * 2 + 1]);
pixels[(i * 4) + 2] = (byte)(((pixel >> 11) & 0x1F) * 8); // Red
pixels[(i * 4) + 1] = (byte)(((pixel >> 6) & 0x1F) * 8); // Green
pixels[(i * 4) + 0] = (byte)(((pixel >> 1) & 0x1F) * 8); // Blue
pixels[(i * 4) + 3] = (pixel & 1) > 0 ? (byte)0xFF : (byte)0x00; // (Transparency)
}
Marshal.Copy(pixels, 0, bitmapData.Scan0, pixels.Length);
tex.UnlockBits(bitmapData);
}
tex.Tag = new string[] { "Format: RGBA16", "Width: " + width,
"Height: " + height };
return tex;
}
public static Bitmap decodeIA16(byte[] data, int width, int height)
{
Bitmap tex = new Bitmap(width, height);
if (data.Length >= width * height * 2) // Sanity Check
{
BitmapData bitmapData = tex.LockBits(new Rectangle(0, 0, width, height),
ImageLockMode.ReadWrite, tex.PixelFormat);
byte[] pixels = new byte[width * height * 4];
int len = width * height;
for (int i = 0; i < len; i++)
{
pixels[(i * 4) + 2] = data[i * 2]; // Red
pixels[(i * 4) + 1] = data[i * 2]; // Green
pixels[(i * 4) + 0] = data[i * 2]; // Blue
pixels[(i * 4) + 3] = data[(i * 2) + 1]; // Alpha
}
Marshal.Copy(pixels, 0, bitmapData.Scan0, pixels.Length);
tex.UnlockBits(bitmapData);
}
tex.Tag = new string[] { "Format: IA16", "Width: " + width,
"Height: " + height};
return tex;
}
public static Bitmap decodeIA8(byte[] data, int width, int height)
{
Bitmap tex = new Bitmap(width, height);
if (data.Length >= width * height) // Sanity Check
{
BitmapData bitmapData = tex.LockBits(new Rectangle(0, 0, width, height),
ImageLockMode.ReadWrite, tex.PixelFormat);
byte[] pixels = new byte[width * height * 4];
int len = width * height;
for (int i = 0; i < len; i++)
{
byte intensity = (byte)(((data[i] >> 4) & 0xF) * 16);
pixels[(i * 4) + 2] = intensity; // Red
pixels[(i * 4) + 1] = intensity; // Green
pixels[(i * 4) + 0] = intensity; // Blue
pixels[(i * 4) + 3] = (byte)((data[i] & 0xF) * 16); // Alpha
}
Marshal.Copy(pixels, 0, bitmapData.Scan0, pixels.Length);
tex.UnlockBits(bitmapData);
}
tex.Tag = new string[] { "Format: IA8", "Width: " + width,
"Height: " + height };
return tex;
}
public static Bitmap decodeIA4(byte[] data, int width, int height)
{
Bitmap tex = new Bitmap(width, height);
if (data.Length >= (width * height) / 2) // Sanity Check
{
BitmapData bitmapData = tex.LockBits(new Rectangle(0, 0, width, height),
ImageLockMode.ReadWrite, tex.PixelFormat);
byte[] pixels = new byte[width * height * 4];
int len = (width * height) / 2;
for (int i = 0; i < len; i++)
{
byte twoPixels = data[i];
byte intensity = (byte)((twoPixels >> 5) * 32);
pixels[(i * 8) + 2] = intensity; // Red
pixels[(i * 8) + 1] = intensity; // Green
pixels[(i * 8) + 0] = intensity; // Blue
pixels[(i * 8) + 3] = (byte)(((twoPixels >> 4) & 0x1) * 255); // Alpha
intensity = (byte)(((twoPixels >> 1) & 0x7) * 32);
pixels[(i * 8) + 6] = intensity; // Red
pixels[(i * 8) + 5] = intensity; // Green
pixels[(i * 8) + 4] = intensity; // Blue
pixels[(i * 8) + 7] = (byte)((twoPixels & 0x1) * 255); // Alpha
}
Marshal.Copy(pixels, 0, bitmapData.Scan0, pixels.Length);
tex.UnlockBits(bitmapData);
tex.Tag = new string[] { "Format: IA4", "Width: " + width,
"Height: " + height };
}
return tex;
}
public static Bitmap decodeI8(byte[] data, int width, int height)
{
Bitmap tex = new Bitmap(width, height);
if (data.Length >= width * height) // Sanity Check
{
BitmapData bitmapData = tex.LockBits(new Rectangle(0, 0, width, height),
ImageLockMode.ReadWrite, tex.PixelFormat);
byte[] pixels = new byte[width * height * 4];
int len = width * height;
for (int i = 0; i < len; i++)
{
byte intensity = data[i];
pixels[(i * 4) + 2] = intensity; // Red
pixels[(i * 4) + 1] = intensity; // Green
pixels[(i * 4) + 0] = intensity; // Blue
pixels[(i * 4) + 3] = 0xFF; // Alpha
}
Marshal.Copy(pixels, 0, bitmapData.Scan0, pixels.Length);
tex.UnlockBits(bitmapData);
tex.Tag = new string[] { "Format: I8", "Width: " + width,
"Height: " + height };
}
return tex;
}
public static Bitmap decodeI4(byte[] data, int width, int height)
{
Bitmap tex = new Bitmap(width, height);
if (data.Length >= (width * height) / 2) // Sanity Check
{
BitmapData bitmapData = tex.LockBits(new Rectangle(0, 0, width, height),
ImageLockMode.ReadWrite, tex.PixelFormat);
byte[] pixels = new byte[width * height * 4];
int len = (width * height) / 2;
for (int i = 0; i < len; i++)
{
byte twoPixels = data[i];
byte intensity = (byte)((twoPixels >> 4) * 16);
pixels[(i * 8) + 2] = intensity; // Red
pixels[(i * 8) + 1] = intensity; // Green
pixels[(i * 8) + 0] = intensity; // Blue
pixels[(i * 8) + 3] = 0xFF; // Alpha
intensity = (byte)((twoPixels & 0xF) * 16);
pixels[(i * 8) + 6] = intensity; // Red
pixels[(i * 8) + 5] = intensity; // Green
pixels[(i * 8) + 4] = intensity; // Blue
pixels[(i * 8) + 7] = 0xFF; // Alpha
}
Marshal.Copy(pixels, 0, bitmapData.Scan0, pixels.Length);
tex.UnlockBits(bitmapData);
}
tex.Tag = new string[] { "Format: I4", "Width: " + width,
"Height: " + height };
return tex;
}
public static Bitmap decodeCI4(byte[] data, int width, int height, ushort[] palette)
{
Bitmap tex = new Bitmap(width, height);
if (data.Length >= (width * height) / 2) // Sanity Check
{
BitmapData bitmapData = tex.LockBits(new Rectangle(0, 0, width, height),
ImageLockMode.ReadWrite, tex.PixelFormat);
byte[] pixels = new byte[width * height * 4];
int len = (width * height) / 2;
for (int i = 0; i < len; i++)
{
ushort pixel = palette[(data[i] >> 4) & 0xF];
pixels[(i * 8) + 2] = (byte)(((pixel >> 11) & 0x1F) * 8); // Red
pixels[(i * 8) + 1] = (byte)(((pixel >> 6) & 0x1F) * 8); // Green
pixels[(i * 8) + 0] = (byte)(((pixel >> 1) & 0x1F) * 8); // Blue
pixels[(i * 8) + 3] = (pixel & 1) > 0 ? (byte)0xFF : (byte)0x00; // Alpha
pixel = palette[(data[i]) & 0xF];
pixels[(i * 8) + 6] = (byte)(((pixel >> 11) & 0x1F) * 8); // Red
pixels[(i * 8) + 5] = (byte)(((pixel >> 6) & 0x1F) * 8); // Green
pixels[(i * 8) + 4] = (byte)(((pixel >> 1) & 0x1F) * 8); // Blue
pixels[(i * 8) + 7] = (pixel & 1) > 0 ? (byte)0xFF : (byte)0x00; // Alpha
}
Marshal.Copy(pixels, 0, bitmapData.Scan0, pixels.Length);
tex.UnlockBits(bitmapData);
}
tex.Tag = new string[] { "Format: CI4", "Width: " + width,
"Height: " + height };
return tex;
}
public static Bitmap decodeCI8(byte[] data, int width, int height, ushort[] palette)
{
Bitmap tex = new Bitmap(width, height);
if (data.Length >= width * height) // Sanity Check
{
BitmapData bitmapData = tex.LockBits(new Rectangle(0, 0, width, height),
ImageLockMode.ReadWrite, tex.PixelFormat);
byte[] pixels = new byte[width * height * 4];
int len = width * height;
for (int i = 0; i < len; i++)
{
ushort pixel = palette[data[i]];
pixels[(i * 4) + 2] = (byte)(((pixel >> 11) & 0x1F) * 8); // Red
pixels[(i * 4) + 1] = (byte)(((pixel >> 6) & 0x1F) * 8); // Green
pixels[(i * 4) + 0] = (byte)(((pixel >> 1) & 0x1F) * 8); // Blue
pixels[(i * 4) + 3] = (pixel & 1) > 0 ? (byte)0xFF : (byte)0x00; // (Transparency)
//tex.SetPixel(i % width, i / width, Color.FromArgb(alpha, red, green, blue));
}
Marshal.Copy(pixels, 0, bitmapData.Scan0, pixels.Length);
tex.UnlockBits(bitmapData);
}
tex.Tag = new string[] { "Format: CI8", "Width: " + width,
"Height: " + height };
return tex;
}
}
}