Projektdateien hinzufügen.
This commit is contained in:
524
SM64Lib.N64Graphics/N64Graphics.cs
Normal file
524
SM64Lib.N64Graphics/N64Graphics.cs
Normal file
@@ -0,0 +1,524 @@
|
||||
using System.Drawing;
|
||||
|
||||
namespace SM64Lib.N64Graphics
|
||||
{
|
||||
public enum N64Codec { RGBA16, RGBA32, IA16, IA8, IA4, I8, I4, CI8, CI4, ONEBPP };
|
||||
public enum N64IMode { AlphaCopyIntensity, AlphaBinary, AlphaOne };
|
||||
|
||||
public class N64Graphics
|
||||
{
|
||||
private static int SCALE_5_8(int val)
|
||||
{
|
||||
return (val * 0xFF) / 0x1F;
|
||||
}
|
||||
|
||||
private static byte SCALE_8_5(byte val)
|
||||
{
|
||||
return (byte)((((val) + 4) * 0x1F) / 0xFF);
|
||||
}
|
||||
|
||||
private static byte SCALE_8_4(byte val)
|
||||
{
|
||||
return (byte)(val / 0x11);
|
||||
}
|
||||
|
||||
private static int SCALE_3_8(byte val)
|
||||
{
|
||||
return (val * 0xFF) / 0x7;
|
||||
}
|
||||
|
||||
private static byte SCALE_8_3(byte val)
|
||||
{
|
||||
return (byte)(val / 0x24);
|
||||
}
|
||||
|
||||
public static Color RGBA16Color(byte c0, byte c1)
|
||||
{
|
||||
int r = SCALE_5_8((c0 & 0xF8) >> 3);
|
||||
int g = SCALE_5_8(((c0 & 0x07) << 2) | ((c1 & 0xC0) >> 6));
|
||||
int b = SCALE_5_8((c1 & 0x3E) >> 1);
|
||||
int a = ((c1 & 0x1) > 0) ? 255 : 0;
|
||||
return Color.FromArgb(a, r, g, b);
|
||||
}
|
||||
|
||||
public static void ColorRGBA16(Color col, out byte c0, out byte c1)
|
||||
{
|
||||
byte r, g, b;
|
||||
r = SCALE_8_5(col.R);
|
||||
g = SCALE_8_5(col.G);
|
||||
b = SCALE_8_5(col.B);
|
||||
c0 = (byte)((r << 3) | (g >> 2));
|
||||
c1 = (byte)(((g & 0x3) << 6) | (b << 1) | ((col.A > 0) ? 1 : 0));
|
||||
}
|
||||
|
||||
public static Color RGBA16Color(byte[] data, int pixOffset)
|
||||
{
|
||||
byte c0 = data[pixOffset];
|
||||
byte c1 = data[pixOffset + 1];
|
||||
|
||||
return RGBA16Color(c0, c1);
|
||||
}
|
||||
|
||||
public static Color RGBA32Color(byte[] data, int pixOffset)
|
||||
{
|
||||
int r, g, b, a;
|
||||
r = data[pixOffset];
|
||||
g = data[pixOffset + 1];
|
||||
b = data[pixOffset + 2];
|
||||
a = data[pixOffset + 3];
|
||||
return Color.FromArgb(a, r, g, b);
|
||||
}
|
||||
|
||||
public static Color IA16Color(byte[] data, int pixOffset)
|
||||
{
|
||||
int i = data[pixOffset];
|
||||
int a = data[pixOffset + 1];
|
||||
return Color.FromArgb(a, i, i, i);
|
||||
}
|
||||
|
||||
public static Color IA8Color(byte[] data, int pixOffset)
|
||||
{
|
||||
int i, a;
|
||||
byte c = data[pixOffset];
|
||||
i = (c >> 4) * 0x11;
|
||||
a = (c & 0xF) * 0x11;
|
||||
return Color.FromArgb(a, i, i, i);
|
||||
}
|
||||
|
||||
public static Color IA4Color(byte[] data, int pixOffset, int nibble)
|
||||
{
|
||||
int shift = (1 - nibble) * 4;
|
||||
int i, a;
|
||||
int val = (data[pixOffset] >> shift) & 0xF;
|
||||
i = SCALE_3_8((byte)(val >> 1));
|
||||
a = (val & 0x1) > 0 ? 0xFF : 0x00;
|
||||
return Color.FromArgb(a, i, i, i);
|
||||
}
|
||||
|
||||
public static Color I8Color(byte[] data, int pixOffset, N64IMode mode = N64IMode.AlphaCopyIntensity)
|
||||
{
|
||||
int i = data[pixOffset];
|
||||
int a = i;
|
||||
switch (mode)
|
||||
{
|
||||
case N64IMode.AlphaBinary: a = (i == 0) ? 0 : 0xFF; break;
|
||||
case N64IMode.AlphaCopyIntensity: a = i; break;
|
||||
case N64IMode.AlphaOne: a = 0xFF; break;
|
||||
}
|
||||
return Color.FromArgb(a, i, i, i);
|
||||
}
|
||||
|
||||
public static Color I4Color(byte[] data, int pixOffset, int nibble, N64IMode mode = N64IMode.AlphaCopyIntensity)
|
||||
{
|
||||
int shift = (1 - nibble) * 4;
|
||||
int i = (data[pixOffset] >> shift) & 0xF;
|
||||
i *= 0x11;
|
||||
int a = i;
|
||||
switch (mode)
|
||||
{
|
||||
case N64IMode.AlphaBinary: a = (i == 0) ? 0 : 0xFF; break;
|
||||
case N64IMode.AlphaCopyIntensity: a = i; break;
|
||||
case N64IMode.AlphaOne: a = 0xFF; break;
|
||||
}
|
||||
return Color.FromArgb(a, i, i, i);
|
||||
}
|
||||
|
||||
public static Color CI8Color(byte[] data, byte[] palette, int pixOffset)
|
||||
{
|
||||
byte c0, c1;
|
||||
int palOffset = 2 * data[pixOffset];
|
||||
c0 = palette[palOffset];
|
||||
c1 = palette[palOffset + 1];
|
||||
|
||||
return RGBA16Color(c0, c1);
|
||||
}
|
||||
|
||||
public static Color CI4Color(byte[] data, byte[] palette, int pixOffset, int nibble)
|
||||
{
|
||||
byte c0, c1;
|
||||
int shift = (1 - nibble) * 4;
|
||||
int palOffset = 2 * ((data[pixOffset] >> shift) & 0xF);
|
||||
c0 = palette[palOffset];
|
||||
c1 = palette[palOffset + 1];
|
||||
|
||||
return RGBA16Color(c0, c1);
|
||||
}
|
||||
|
||||
public static Color BPPColor(byte[] data, int pixOffset, int bit)
|
||||
{
|
||||
int i, a;
|
||||
int val = (data[pixOffset] >> (7 - bit)) & 0x1;
|
||||
i = a = val == 0 ? 0x00 : 0xFF;
|
||||
return Color.FromArgb(a, i, i, i);
|
||||
}
|
||||
|
||||
// return number of bytes needed to encode numPixels using codec
|
||||
public static int PixelsToBytes(N64Codec codec, int numPixels)
|
||||
{
|
||||
int numBytes = 0;
|
||||
switch (codec)
|
||||
{
|
||||
case N64Codec.RGBA16: numBytes = numPixels * 2; break;
|
||||
case N64Codec.RGBA32: numBytes = numPixels * 4; break;
|
||||
case N64Codec.IA16: numBytes = numPixels * 2; break;
|
||||
case N64Codec.IA8: numBytes = numPixels; break;
|
||||
case N64Codec.IA4: numBytes = numPixels / 2; break;
|
||||
case N64Codec.I8: numBytes = numPixels; break;
|
||||
case N64Codec.I4: numBytes = numPixels / 2; break;
|
||||
case N64Codec.CI8: numBytes = numPixels; break;
|
||||
case N64Codec.CI4: numBytes = numPixels / 2; break;
|
||||
case N64Codec.ONEBPP: numBytes = numPixels / 8; break;
|
||||
}
|
||||
return numBytes;
|
||||
}
|
||||
|
||||
public static string CodecString(N64Codec codec)
|
||||
{
|
||||
switch (codec)
|
||||
{
|
||||
case N64Codec.RGBA16: return "rgba16";
|
||||
case N64Codec.RGBA32: return "rgba32";
|
||||
case N64Codec.IA16: return "ia16";
|
||||
case N64Codec.IA8: return "ia8";
|
||||
case N64Codec.IA4: return "ia4";
|
||||
case N64Codec.I8: return "i8";
|
||||
case N64Codec.I4: return "i4";
|
||||
case N64Codec.CI8: return "ci8";
|
||||
case N64Codec.CI4: return "ci4";
|
||||
case N64Codec.ONEBPP: return "1bpp";
|
||||
}
|
||||
return "unk";
|
||||
}
|
||||
|
||||
public static N64Codec StringCodec(string str)
|
||||
{
|
||||
switch (str.ToLower())
|
||||
{
|
||||
default:
|
||||
case "rgba16": return N64Codec.RGBA16;
|
||||
case "rgba32": return N64Codec.RGBA32;
|
||||
case "ia16": return N64Codec.IA16;
|
||||
case "ia8": return N64Codec.IA8;
|
||||
case "ia4": return N64Codec.IA4;
|
||||
case "i8": return N64Codec.I8;
|
||||
case "i4": return N64Codec.I4;
|
||||
case "ci8": return N64Codec.CI8;
|
||||
case "ci4": return N64Codec.CI4;
|
||||
case "1bpp": return N64Codec.ONEBPP;
|
||||
}
|
||||
}
|
||||
|
||||
public static Color MakeColor(byte[] data, byte[] palette, int offset, int select, N64Codec codec, N64IMode mode)
|
||||
{
|
||||
Color color;
|
||||
switch (codec)
|
||||
{
|
||||
case N64Codec.RGBA16:
|
||||
color = RGBA16Color(data, offset);
|
||||
break;
|
||||
case N64Codec.RGBA32:
|
||||
color = RGBA32Color(data, offset);
|
||||
break;
|
||||
case N64Codec.IA16:
|
||||
color = IA16Color(data, offset);
|
||||
break;
|
||||
case N64Codec.IA8:
|
||||
color = IA8Color(data, offset);
|
||||
break;
|
||||
case N64Codec.IA4:
|
||||
color = IA4Color(data, offset, select);
|
||||
break;
|
||||
case N64Codec.I8:
|
||||
color = I8Color(data, offset, mode);
|
||||
break;
|
||||
case N64Codec.I4:
|
||||
color = I4Color(data, offset, select, mode);
|
||||
break;
|
||||
case N64Codec.CI8:
|
||||
color = CI8Color(data, palette, offset);
|
||||
break;
|
||||
case N64Codec.CI4:
|
||||
color = CI4Color(data, palette, offset, select);
|
||||
break;
|
||||
case N64Codec.ONEBPP:
|
||||
color = BPPColor(data, offset, select);
|
||||
break;
|
||||
default:
|
||||
color = RGBA16Color(data, offset);
|
||||
break;
|
||||
}
|
||||
return color;
|
||||
}
|
||||
|
||||
public static void RenderTexture(Graphics g, byte[] data, byte[] palette, int offset, int width, int height, int scale, N64Codec codec, N64IMode mode)
|
||||
{
|
||||
Brush brush;
|
||||
for (int h = 0; h < height; h++)
|
||||
{
|
||||
for (int w = 0; w < width; w++)
|
||||
{
|
||||
int pixOffset = (h * width + w);
|
||||
int bytesPerPix = 1;
|
||||
int select = 0;
|
||||
switch (codec)
|
||||
{
|
||||
case N64Codec.RGBA16: bytesPerPix = 2; pixOffset *= bytesPerPix; break;
|
||||
case N64Codec.RGBA32: bytesPerPix = 4; pixOffset *= bytesPerPix; break;
|
||||
case N64Codec.IA16: bytesPerPix = 2; pixOffset *= bytesPerPix; break;
|
||||
case N64Codec.IA8: break;
|
||||
case N64Codec.IA4:
|
||||
select = pixOffset & 0x1;
|
||||
pixOffset /= 2;
|
||||
break;
|
||||
case N64Codec.I8: break;
|
||||
case N64Codec.I4:
|
||||
case N64Codec.CI4:
|
||||
select = pixOffset & 0x1;
|
||||
pixOffset /= 2;
|
||||
break;
|
||||
case N64Codec.CI8: break;
|
||||
case N64Codec.ONEBPP:
|
||||
select = pixOffset & 0x7;
|
||||
pixOffset /= 8;
|
||||
break;
|
||||
}
|
||||
pixOffset += offset;
|
||||
if (data.Length > pixOffset + bytesPerPix - 1)
|
||||
{
|
||||
brush = new SolidBrush(MakeColor(data, palette, pixOffset, select, codec, mode));
|
||||
g.FillRectangle(brush, w * scale, h * scale, scale, scale);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// return palette index of matching c0/c1 16-bit dataset, or -1 if not found
|
||||
private static int paletteIndex(byte[] pal, int palCount, byte c0, byte c1)
|
||||
{
|
||||
for (int i = 0; i < palCount; i++)
|
||||
{
|
||||
if (pal[2 * i] == c0 && pal[2 * i + 1] == c1)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public static void Convert(ref byte[] imageData, ref byte[] paletteData, N64Codec codec, Bitmap bm)
|
||||
{
|
||||
int numPixels = bm.Width * bm.Height;
|
||||
imageData = new byte[PixelsToBytes(codec, numPixels)];
|
||||
int palCount = 0;
|
||||
switch (codec)
|
||||
{
|
||||
case N64Codec.RGBA16:
|
||||
for (int y = 0; y < bm.Height; y++)
|
||||
{
|
||||
for (int x = 0; x < bm.Width; x++)
|
||||
{
|
||||
Color col = bm.GetPixel(x, y);
|
||||
byte c0;
|
||||
byte c1;
|
||||
ColorRGBA16(col, out c0, out c1);
|
||||
int idx = 2 * (y * bm.Width + x);
|
||||
imageData[idx + 0] = c0;
|
||||
imageData[idx + 1] = c1;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case N64Codec.RGBA32:
|
||||
for (int y = 0; y < bm.Height; y++)
|
||||
{
|
||||
for (int x = 0; x < bm.Width; x++)
|
||||
{
|
||||
Color col = bm.GetPixel(x, y);
|
||||
int idx = 4 * (y * bm.Width + x);
|
||||
imageData[idx + 0] = col.R;
|
||||
imageData[idx + 1] = col.G;
|
||||
imageData[idx + 2] = col.B;
|
||||
imageData[idx + 3] = col.A;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case N64Codec.IA16:
|
||||
for (int y = 0; y < bm.Height; y++)
|
||||
{
|
||||
for (int x = 0; x < bm.Width; x++)
|
||||
{
|
||||
Color col = bm.GetPixel(x, y);
|
||||
int sum = col.R + col.G + col.B;
|
||||
byte intensity = (byte)(sum / 3);
|
||||
byte alpha = col.A;
|
||||
int idx = 2 * (y * bm.Width + x);
|
||||
imageData[idx + 0] = intensity;
|
||||
imageData[idx + 1] = alpha;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case N64Codec.IA8:
|
||||
for (int y = 0; y < bm.Height; y++)
|
||||
{
|
||||
for (int x = 0; x < bm.Width; x++)
|
||||
{
|
||||
Color col = bm.GetPixel(x, y);
|
||||
int sum = col.R + col.G + col.B;
|
||||
byte intensity = SCALE_8_4((byte)(sum / 3));
|
||||
byte alpha = SCALE_8_4(col.A);
|
||||
int idx = y * bm.Width + x;
|
||||
imageData[idx] = (byte)((intensity << 4) | alpha);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case N64Codec.IA4:
|
||||
for (int y = 0; y < bm.Height; y++)
|
||||
{
|
||||
for (int x = 0; x < bm.Width; x++)
|
||||
{
|
||||
Color col = bm.GetPixel(x, y);
|
||||
int sum = col.R + col.G + col.B;
|
||||
byte intensity = SCALE_8_3((byte)(sum / 3));
|
||||
byte alpha = (byte)(col.A > 0 ? 1 : 0);
|
||||
int idx = y * bm.Width + x;
|
||||
byte old = imageData[idx / 2];
|
||||
if ((idx % 2) > 0)
|
||||
{
|
||||
imageData[idx / 2] = (byte)((old & 0xF0) | (intensity << 1) | alpha);
|
||||
}
|
||||
else
|
||||
{
|
||||
imageData[idx / 2] = (byte)((old & 0x0F) | (((intensity << 1) | alpha) << 4));
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case N64Codec.I8:
|
||||
for (int y = 0; y < bm.Height; y++)
|
||||
{
|
||||
for (int x = 0; x < bm.Width; x++)
|
||||
{
|
||||
Color col = bm.GetPixel(x, y);
|
||||
int sum = col.R + col.G + col.B;
|
||||
byte intensity = (byte)(sum / 3);
|
||||
int idx = y * bm.Width + x;
|
||||
imageData[idx] = intensity;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case N64Codec.I4:
|
||||
for (int y = 0; y < bm.Height; y++)
|
||||
{
|
||||
for (int x = 0; x < bm.Width; x++)
|
||||
{
|
||||
Color col = bm.GetPixel(x, y);
|
||||
int sum = col.R + col.G + col.B;
|
||||
byte intensity = SCALE_8_4((byte)(sum / 3));
|
||||
int idx = y * bm.Width + x;
|
||||
byte old = imageData[idx / 2];
|
||||
if ((idx % 2) > 0)
|
||||
{
|
||||
imageData[idx / 2] = (byte)((old & 0xF0) | intensity);
|
||||
}
|
||||
else
|
||||
{
|
||||
imageData[idx / 2] = (byte)((old & 0x0F) | (intensity << 4));
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case N64Codec.CI4:
|
||||
paletteData = new byte[16 * 2];
|
||||
for (int y = 0; y < bm.Height; y++)
|
||||
{
|
||||
for (int x = 0; x < bm.Width; x++)
|
||||
{
|
||||
Color col = bm.GetPixel(x, y);
|
||||
byte r, g, b;
|
||||
r = SCALE_8_5(col.R);
|
||||
g = SCALE_8_5(col.G);
|
||||
b = SCALE_8_5(col.B);
|
||||
byte c0 = (byte)((r << 3) | (g >> 2));
|
||||
byte c1 = (byte)(((g & 0x3) << 6) | (b << 1) | ((col.A > 0) ? 1 : 0));
|
||||
int idx = y * bm.Width + x;
|
||||
int palIdx = paletteIndex(paletteData, palCount, c0, c1);
|
||||
if (palIdx < 0)
|
||||
{
|
||||
if (palCount < paletteData.Length / 2)
|
||||
{
|
||||
palIdx = palCount;
|
||||
paletteData[2 * palCount] = c0;
|
||||
paletteData[2 * palCount + 1] = c1;
|
||||
palCount++;
|
||||
}
|
||||
else
|
||||
{
|
||||
palIdx = 0;
|
||||
// TODO: out of palette entries. error or pick closest?
|
||||
}
|
||||
}
|
||||
byte old = imageData[idx / 2];
|
||||
if ((idx % 2) > 0)
|
||||
{
|
||||
imageData[idx / 2] = (byte)((old & 0xF0) | (byte)palIdx);
|
||||
}
|
||||
else
|
||||
{
|
||||
imageData[idx / 2] = (byte)((old & 0x0F) | ((byte)palIdx << 4));
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case N64Codec.CI8:
|
||||
paletteData = new byte[256 * 2];
|
||||
for (int y = 0; y < bm.Height; y++)
|
||||
{
|
||||
for (int x = 0; x < bm.Width; x++)
|
||||
{
|
||||
Color col = bm.GetPixel(x, y);
|
||||
byte r, g, b;
|
||||
r = SCALE_8_5(col.R);
|
||||
g = SCALE_8_5(col.G);
|
||||
b = SCALE_8_5(col.B);
|
||||
byte c0 = (byte)((r << 3) | (g >> 2));
|
||||
byte c1 = (byte)(((g & 0x3) << 6) | (b << 1) | ((col.A > 0) ? 1 : 0));
|
||||
int idx = y * bm.Width + x;
|
||||
int palIdx = paletteIndex(paletteData, palCount, c0, c1);
|
||||
if (palIdx < 0)
|
||||
{
|
||||
if (palCount < paletteData.Length / 2)
|
||||
{
|
||||
palIdx = palCount;
|
||||
paletteData[2 * palCount] = c0;
|
||||
paletteData[2 * palCount + 1] = c1;
|
||||
palCount++;
|
||||
}
|
||||
else
|
||||
{
|
||||
palIdx = 0;
|
||||
// TODO: out of palette entries. error or pick closest?
|
||||
}
|
||||
}
|
||||
imageData[idx] = (byte)palIdx;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case N64Codec.ONEBPP:
|
||||
for (int y = 0; y < bm.Height; y++)
|
||||
{
|
||||
for (int x = 0; x < bm.Width; x++)
|
||||
{
|
||||
Color col = bm.GetPixel(x, y);
|
||||
int sum = col.R + col.G + col.B;
|
||||
byte intensity = (sum > 0) ? (byte)1 : (byte)0;
|
||||
int idx = y * bm.Width + x;
|
||||
byte old = imageData[idx / 8];
|
||||
int bit = idx % 8;
|
||||
int mask = ~(1 << bit);
|
||||
imageData[idx / 8] = (byte)((old & mask) | (intensity << bit));
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user