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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
11
SM64Lib.N64Graphics/Properties/AssemblyInfo.cs
Normal file
11
SM64Lib.N64Graphics/Properties/AssemblyInfo.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// Durch Festlegen von ComVisible auf "false" werden die Typen in dieser Assembly unsichtbar
|
||||
// für COM-Komponenten. Wenn Sie auf einen Typ in dieser Assembly von
|
||||
// COM aus zugreifen müssen, sollten Sie das ComVisible-Attribut für diesen Typ auf "True" festlegen.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// Die folgende GUID bestimmt die ID der Typbibliothek, wenn dieses Projekt für COM verfügbar gemacht wird
|
||||
[assembly: Guid("d9258483-950c-46d1-a333-31f32492cd7e")]
|
||||
23
SM64Lib.N64Graphics/SM64Lib.N64Graphics.csproj
Normal file
23
SM64Lib.N64Graphics/SM64Lib.N64Graphics.csproj
Normal file
@@ -0,0 +1,23 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0-windows</TargetFramework>
|
||||
<AssemblyTitle>N64Graphics</AssemblyTitle>
|
||||
<Company>Pilzinsel64</Company>
|
||||
<Product>N64Graphics</Product>
|
||||
<Copyright>Copyright © Pilzinsel64 2017 - 2018</Copyright>
|
||||
<UseWindowsForms>true</UseWindowsForms>
|
||||
<Platforms>AnyCPU</Platforms>
|
||||
<Configurations>Debug;Release;ReleaseBundle;ReleaseStandalone</Configurations>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.VisualBasic" Version="10.3.0" />
|
||||
<PackageReference Include="System.Data.DataSetExtensions" Version="4.5.0" />
|
||||
<PackageReference Include="Microsoft.CSharp" Version="4.7.0" />
|
||||
<PackageReference Include="System.Net.Http" Version="4.3.4" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Remove="obj\RelMono\TemporaryGeneratedFile_036C0B5B-1481-4323-8D20-8F5ADCB23D92.cs" />
|
||||
<Compile Remove="obj\RelMono\TemporaryGeneratedFile_5937a670-0e60-4077-877b-f7221da3dda1.cs" />
|
||||
<Compile Remove="obj\RelMono\TemporaryGeneratedFile_E7A71F73-0F8D-4B9B-B56E-8E70B10BC5D3.cs" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
598
SM64Lib.N64Graphics/TextureFormat.cs
Normal file
598
SM64Lib.N64Graphics/TextureFormat.cs
Normal file
@@ -0,0 +1,598 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user