public ColorIndexConverter(uint[] rgbaData, int width, int height, GXTexFmt tplFormat, GXTlutFmt paletteFormat) { if (tplFormat != GXTexFmt.CI4 && tplFormat != GXTexFmt.CI8) // && tplFormat != TPL_TextureFormat.CI14X2) { throw new Exception("Texture format must be either CI4 or CI8"); // or CI14X2!"); } if (paletteFormat != GXTlutFmt.IA8 && paletteFormat != GXTlutFmt.RGB565 && paletteFormat != GXTlutFmt.RGB5A3) { throw new Exception("Palette format must be either IA8, RGB565 or RGB5A3!"); } this.rgbaData = rgbaData; this.width = width; this.height = height; this.tplFormat = tplFormat; this.paletteFormat = paletteFormat; buildPalette(); if (tplFormat == GXTexFmt.CI4) { toCI4(); } else if (tplFormat == GXTexFmt.CI8) { toCI8(); } else { toCI14X2(); } }
/// <summary> /// /// </summary> /// <param name="name"></param> /// <param name="texFmt"></param> /// <param name="palFmt"></param> public static bool FormatFromString(string name, out GXTexFmt texFmt, out GXTlutFmt palFmt) { texFmt = GXTexFmt.RGBA8; palFmt = GXTlutFmt.RGB5A3; var parts = Path.GetFileNameWithoutExtension(name).Split('_'); bool foundFormat = false; foreach (var p in parts) { // skip numeric values if (int.TryParse(p, out int i)) { continue; } if (Enum.TryParse(p.ToUpper(), out GXTexFmt format)) { texFmt = format; foundFormat = true; } if (Enum.TryParse(p.ToUpper(), out GXTlutFmt palFormat)) { palFmt = palFormat; } } return(foundFormat); }
public override void Open(HSDReader Reader) { uint DataOffset = Reader.ReadUInt32(); Format = (GXTlutFmt)Reader.ReadUInt32(); GXTlut = Reader.ReadUInt32(); ColorCount = Reader.ReadUInt16(); Reader.ReadUInt16(); Data = Reader.ReadBuffer(DataOffset, TPL.PaletteByteSize((TPL_PaletteFormat)Format, ColorCount)); }
private static uint[] PaletteToRGBA(GXTlutFmt format, int count, byte[] data) { int itemcount = count; int r, g, b, a; uint[] output = new uint[itemcount]; for (int i = 0; i < itemcount; i++) { if (i >= itemcount) { continue; } ushort pixel = BitConverter.ToUInt16(new byte[] { data[i * 2 + 1], data[i * 2] }, 0); if (format == GXTlutFmt.IA8) //IA8 { r = pixel & 0xff; b = r; g = r; a = pixel >> 8; } else if (format == GXTlutFmt.RGB565) //RGB565 { b = (((pixel >> 11) & 0x1F) << 3) & 0xff; g = (((pixel >> 5) & 0x3F) << 2) & 0xff; r = (((pixel >> 0) & 0x1F) << 3) & 0xff; a = 255; } else //RGB5A3 { if ((pixel & (1 << 15)) != 0) //RGB555 { a = 255; b = (((pixel >> 10) & 0x1F) * 255) / 31; g = (((pixel >> 5) & 0x1F) * 255) / 31; r = (((pixel >> 0) & 0x1F) * 255) / 31; } else //RGB4A3 { a = (((pixel >> 12) & 0x07) * 255) / 7; b = (((pixel >> 8) & 0x0F) * 255) / 15; g = (((pixel >> 4) & 0x0F) * 255) / 15; r = (((pixel >> 0) & 0x0F) * 255) / 15; } } output[i] = (uint)((r << 0) | (g << 8) | (b << 16) | (a << 24)); } return(output); }
/// <summary> /// /// </summary> /// <param name="paletteFormat"></param> /// <param name="col"></param> /// <param name="rawColorData"></param> private static void EncodeColor(GXTlutFmt paletteFormat, Color32 col, SortedList <ushort, Color32> rawColorData) { switch (paletteFormat) { case GXTlutFmt.IA8: byte i = (byte)((col.R * 0.2126) + (col.G * 0.7152) + (col.B * 0.0722)); ushort fullIA8 = (ushort)((i << 8) | (col.A)); if (!rawColorData.ContainsKey(fullIA8)) { rawColorData.Add((ushort)(fullIA8), col); } break; case GXTlutFmt.RGB565: ushort r_565 = (ushort)(col.R >> 3); ushort g_565 = (ushort)(col.G >> 2); ushort b_565 = (ushort)(col.B >> 3); ushort fullColor565 = 0; fullColor565 |= b_565; fullColor565 |= (ushort)(g_565 << 5); fullColor565 |= (ushort)(r_565 << 11); if (!rawColorData.ContainsKey(fullColor565)) { rawColorData.Add(fullColor565, col); } break; case GXTlutFmt.RGB5A3: ushort r_53 = (ushort)(col.R >> 4); ushort g_53 = (ushort)(col.G >> 4); ushort b_53 = (ushort)(col.B >> 4); ushort a_53 = (ushort)(col.A >> 5); ushort fullColor53 = 0; fullColor53 |= b_53; fullColor53 |= (ushort)(g_53 << 4); fullColor53 |= (ushort)(r_53 << 8); fullColor53 |= (ushort)(a_53 << 12); if (!rawColorData.ContainsKey(fullColor53)) { rawColorData.Add(fullColor53, col); } break; } }
/// <summary> /// Sets the image data from a <see cref="Bitmap"/> /// </summary> /// <param name="b"></param> public void SetFromBitmap(Bitmap b, GXTexFmt imageFormat, GXTlutFmt paletteFormat) { byte[] palData; ImageData = new HSD_Image(); ImageData.Width = (ushort)b.Width; ImageData.Height = (ushort)b.Height; ImageData.Format = imageFormat; ImageData.Data = TPL.ConvertToTextureMelee(b, (int)imageFormat, (int)paletteFormat, out palData); if (palData != null && palData.Length > 0) { Tlut = new HSD_Tlut(); Tlut.Format = paletteFormat; Tlut.ColorCount = (ushort)(palData.Length / 2); Tlut.Data = palData; } }
/// <summary> /// Import TOBJ from PNG file /// </summary> /// <returns></returns> public static HSD_TOBJ ImportTOBJFromFile(string filePath, GXTexFmt imgFmt, GXTlutFmt tlutFmt) { var TOBJ = new HSD_TOBJ() { MagFilter = GXTexFilter.GX_LINEAR, HScale = 1, WScale = 1, WrapS = GXWrapMode.CLAMP, WrapT = GXWrapMode.CLAMP, SX = 1, SY = 1, SZ = 1, GXTexGenSrc = 4, Blending = 1 }; InjectBitmap(TOBJ, filePath, imgFmt, tlutFmt); return(TOBJ); }
/// <summary> /// /// </summary> /// <returns></returns> public static HSD_TOBJ ToTOBJ(this Bitmap bmp, GXTexFmt texFmt, GXTlutFmt palFmt) { var tobj = new HSD_TOBJ() { MagFilter = GXTexFilter.GX_LINEAR, Flags = TOBJ_FLAGS.COORD_UV | TOBJ_FLAGS.LIGHTMAP_DIFFUSE | TOBJ_FLAGS.COLORMAP_MODULATE | TOBJ_FLAGS.ALPHAMAP_MODULATE, HScale = 1, WScale = 1, WrapS = GXWrapMode.CLAMP, WrapT = GXWrapMode.CLAMP, SX = 1, SY = 1, SZ = 1, GXTexGenSrc = 4, Blending = 1 }; tobj.EncodeImageData(bmp.GetBGRAData(), bmp.Width, bmp.Height, texFmt, palFmt); return(tobj); }
/// <summary> /// Import TOBJ from PNG file /// </summary> /// <returns></returns> public static HSD_TOBJ ImportTOBJFromFile(string filePath, GXTexFmt imgFmt, GXTlutFmt tlutFmt) { var TOBJ = new HSD_TOBJ() { MagFilter = GXTexFilter.GX_LINEAR, Flags = TOBJ_FLAGS.COORD_UV | TOBJ_FLAGS.LIGHTMAP_DIFFUSE | TOBJ_FLAGS.COLORMAP_MODULATE | TOBJ_FLAGS.ALPHAMAP_MODULATE, HScale = 1, WScale = 1, WrapS = GXWrapMode.CLAMP, WrapT = GXWrapMode.CLAMP, SX = 1, SY = 1, SZ = 1, GXTexGenSrc = 4, Blending = 1 }; InjectBitmap(filePath, TOBJ, imgFmt, tlutFmt); return(TOBJ); }
/// <summary> /// /// </summary> /// <param name="b"></param> public static HSD_TOBJ BitmapToTOBJ(Bitmap bmp, GXTexFmt imgFormat, GXTlutFmt palFormat) { var TOBJ = new HSD_TOBJ() { MagFilter = GXTexFilter.GX_LINEAR, Flags = TOBJ_FLAGS.COORD_UV | TOBJ_FLAGS.LIGHTMAP_DIFFUSE | TOBJ_FLAGS.COLORMAP_MODULATE | TOBJ_FLAGS.ALPHAMAP_MODULATE, HScale = 1, WScale = 1, WrapS = GXWrapMode.CLAMP, WrapT = GXWrapMode.CLAMP, SX = 1, SY = 1, SZ = 1, GXTexGenSrc = 4, Blending = 1 }; InjectBitmap(bmp, TOBJ, imgFormat, palFormat); return(TOBJ); }
/// <summary> /// /// </summary> /// <param name="tobj"></param> /// <param name="bmp"></param> /// <param name="imgFormat"></param> /// <param name="palFormat"></param> public static void InjectBitmap(Bitmap bmp, HSD_TOBJ tobj, GXTexFmt imgFormat, GXTlutFmt palFormat) { tobj.EncodeImageData(bmp.GetBGRAData(), bmp.Width, bmp.Height, imgFormat, palFormat); }
/// <summary> /// /// </summary> /// <param name="data"></param> /// <param name="width"></param> /// <param name="height"></param> /// <param name="imgFormat"></param> /// <param name="palFormat"></param> /// <returns></returns> public static byte[] Encode(byte[] rgba, int width, int height, GXTexFmt imgFormat, GXTlutFmt palFormat, out byte[] palette) { palette = null; if (typeToConverter.ContainsKey(imgFormat)) { return(typeToConverter[imgFormat].ConvertTo(rgba, width, height, null)); } switch (imgFormat) { case GXTexFmt.CI4: { var encode = EncodeC4(rgba, width, height, palFormat); palette = new byte[encode.Item2.Length * sizeof(short)]; Buffer.BlockCopy(encode.Item2, 0, palette, 0, palette.Length); return(encode.Item1); } case GXTexFmt.CI8: { var encode = EncodeC8(rgba, width, height, palFormat); palette = new byte[encode.Item2.Length * sizeof(short)]; Buffer.BlockCopy(encode.Item2, 0, palette, 0, palette.Length); return(encode.Item1); } case GXTexFmt.CI14X2: { // TODO: } break; } return(null); }
private static byte[] DecodeC8(byte[] imgData, uint width, uint height, byte[] imagePalette, GXTlutFmt paletteFormat) { //4 bpp, 8 block width/4 block height, block size 32 bytes, possible palettes (IA8, RGB565, RGB5A3) uint numBlocksW = width / 8; uint numBlocksH = height / 4; int i = 0; byte[] decodedData = new byte[width * height * 8]; //Read the indexes from the file for (int yBlock = 0; yBlock < numBlocksH; yBlock++) { for (int xBlock = 0; xBlock < numBlocksW; xBlock++) { //Inner Loop for pixels for (int pY = 0; pY < 4; pY++) { for (int pX = 0; pX < 8; pX++) { //Ensure we're not reading past the end of the image. if ((xBlock * 8 + pX >= width) || (yBlock * 4 + pY >= height)) { continue; } byte data = imgData[i++]; decodedData[width * ((yBlock * 4) + pY) + (xBlock * 8) + pX] = data; } } } } //Now look them up in the palette and turn them into actual colors. byte[] finalDest = new byte[decodedData.Length / 2]; int pixelSize = paletteFormat == GXTlutFmt.IA8 ? 2 : 4; int destOffset = 0; for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { UnpackPixelFromPalette(decodedData[y * width + x], ref finalDest, destOffset, imagePalette, paletteFormat); destOffset += pixelSize; } } return(finalDest); }
/// <summary> /// /// </summary> /// <param name="format"></param> /// <param name="width"></param> /// <param name="height"></param> /// <param name="imageData"></param> /// <param name="palformat"></param> /// <param name="colorCount"></param> /// <param name="paletteData"></param> /// <returns></returns> public static byte[] DecodeTPL(GXTexFmt format, int width, int height, byte[] imageData, GXTlutFmt palformat, int colorCount, byte[] paletteData) { var paletteDataRgba = new uint[0]; if (IsPalettedFormat(format)) { paletteDataRgba = PaletteToRGBA(palformat, colorCount, paletteData); } byte[] rgba; switch (format) { case GXTexFmt.I4: rgba = fromI4(imageData, width, height); break; case GXTexFmt.I8: rgba = fromI8(imageData, width, height); break; case GXTexFmt.IA4: rgba = fromIA4(imageData, width, height); break; case GXTexFmt.IA8: rgba = fromIA8(imageData, width, height); break; case GXTexFmt.RGB565: rgba = fromRGB565(imageData, width, height); break; case GXTexFmt.RGB5A3: rgba = fromRGB5A3(imageData, width, height); break; case GXTexFmt.RGBA8: rgba = fromRGBA8(imageData, width, height); break; case GXTexFmt.CI4: rgba = new byte[0]; rgba = fromCI4(imageData, paletteDataRgba, width, height); break; case GXTexFmt.CI8: rgba = new byte[0]; rgba = fromCI8(imageData, paletteDataRgba, width, height); break; case GXTexFmt.CI14X2: rgba = new byte[0]; rgba = fromCI14X2(imageData, paletteDataRgba, width, height); break; case GXTexFmt.CMP: rgba = fromCMP(imageData, width, height); break; default: rgba = new byte[0]; break; } return(rgba); }
/// <summary> /// /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void buttonAdd_Click(object sender, EventArgs e) { //if (textureList.SelectedIndices == null || textureList.SelectedIndices.Count == 0) // return; //var index = textureList.SelectedIndices[0]; var tobjs = GetTOBJs(); HSD_TOBJ tobj; var f = Tools.FileIO.OpenFiles(ApplicationSettings.ImageFileFilter); if (f != null) { GXTexFmt texFormat = GXTexFmt.CMP; GXTlutFmt palFormat = GXTlutFmt.IA8; if (tobjs.Length == 0 || tobjs[0].ImageData == null) { using (TextureImportDialog settings = new TextureImportDialog()) { if (settings.ShowDialog() == DialogResult.OK) { texFormat = settings.TextureFormat; palFormat = settings.PaletteFormat; tobjs = new HSD_TOBJ[f.Length]; int ti = 0; foreach (var fi in f) { tobj = new HSD_TOBJ(); TOBJConverter.InjectBitmap(fi, tobj, texFormat, palFormat); tobjs[ti++] = tobj; } } else { return; } } } else { texFormat = tobjs[0].ImageData.Format; if (tobjs[0].TlutData != null) { palFormat = tobjs[0].TlutData.Format; } foreach (var fi in f) { tobj = new HSD_TOBJ(); TOBJConverter.InjectBitmap(fi, tobj, texFormat, palFormat); /*if (tobj.ImageData.Width != tobjs[0].ImageData.Width || tobj.ImageData.Height != tobjs[0].ImageData.Height) * { * MessageBox.Show($"Error the texture size does not match\n{tobj.ImageData.Width}x{tobj.ImageData.Height} -> {tobjs[0].ImageData.Width}x{tobjs[0].ImageData.Height}"); * return; * }*/ tobjs = InsertAt(tobjs, tobj, tobjs.Length); } } } else { return; } SetTOBJs(tobjs); ReloadTextures(); }
/// <summary> /// /// </summary> /// <param name="tobj"></param> /// <param name="bmp"></param> /// <param name="imgFormat"></param> /// <param name="palFormat"></param> public static void InjectBitmap(Bitmap bmp, HSD_TOBJ tobj, GXTexFmt imgFormat, GXTlutFmt palFormat) { // todo: this only works without alpha :/ /*if (imgFormat == GXTexFmt.CI8) // doesn't work well with alpha * bmp = BitmapTools.ReduceColors(bmp, 256); * if (imgFormat == GXTexFmt.CI4 || imgFormat == GXTexFmt.CI14X2) * bmp = BitmapTools.ReduceColors(bmp, 16);*/ tobj.EncodeImageData(bmp.GetBGRAData(), bmp.Width, bmp.Height, imgFormat, palFormat); // dispose if we use our color reduced bitmap //if (imgFormat == GXTexFmt.CI8 || imgFormat == GXTexFmt.CI4 || imgFormat == GXTexFmt.CI14X2) // bmp.Dispose(); }
/// <summary> /// /// </summary> /// <param name="data"></param> /// <param name="width"></param> /// <param name="height"></param> /// <param name="imgFormat"></param> /// <param name="palFormat"></param> /// <returns></returns> public static byte[] Decode(byte[] data, byte[] palData, int width, int height, GXTexFmt imgFormat, GXTlutFmt palFormat) { if (typeToConverter.ContainsKey(imgFormat)) { return(typeToConverter[imgFormat].ConvertFrom(data, width, height, null)); } switch (imgFormat) { case GXTexFmt.CI4: return(DecodeC4(data, (uint)width, (uint)height, palData, palFormat)); case GXTexFmt.CI8: return(DecodeC8(data, (uint)width, (uint)height, palData, palFormat)); case GXTexFmt.CI14X2: // TODO: break; } return(null); }
/// <summary> /// Injects <see cref="Bitmap"/> into <see cref="HSD_TOBJ"/> /// </summary> /// <param name="tobj"></param> /// <param name="img"></param> /// <param name="imgFormat"></param> /// <param name="palFormat"></param> public static void InjectBitmap(string filepath, HSD_TOBJ tobj, GXTexFmt imgFormat, GXTlutFmt palFormat) { // format override if (FormatFromString(filepath, out GXTexFmt fmt, out GXTlutFmt pal)) { palFormat = pal; imgFormat = fmt; } using (Bitmap bmp = LoadBitmapFromFile(filepath)) { InjectBitmap(bmp, tobj, imgFormat, palFormat); } }
private static byte[] FromImage(uint[] rgba, int width, int height, GXTexFmt format, GXTlutFmt palFormat, out byte[] palData) { palData = new byte[0]; if (IsPalettedFormat(format)) { ColorIndexConverter cic = new ColorIndexConverter(rgba, width, height, format, palFormat); palData = cic.Palette; return(cic.Data); } else { return(ImageToTPL(rgba, width, height, format)); } }
/// <summary> /// Injects <see cref="Bitmap"/> into <see cref="HSD_TOBJ"/> /// </summary> /// <param name="tobj"></param> /// <param name="img"></param> /// <param name="imgFormat"></param> /// <param name="palFormat"></param> public static void InjectBitmap(HSD_TOBJ tobj, string filepath, GXTexFmt imgFormat, GXTlutFmt palFormat) { Bitmap bmp = new Bitmap(filepath); InjectBitmap(tobj, bmp, imgFormat, palFormat); bmp.Dispose(); }
/// <summary> /// /// </summary> /// <param name="paletteIndex"></param> /// <param name="dest"></param> /// <param name="offset"></param> /// <param name="paletteData"></param> /// <param name="format"></param> private static void UnpackPixelFromPalette(int paletteIndex, ref byte[] dest, int offset, byte[] paletteData, GXTlutFmt format) { switch (format) { case GXTlutFmt.IA8: dest[0] = paletteData[2 * paletteIndex + 1]; dest[1] = paletteData[2 * paletteIndex + 0]; break; case GXTlutFmt.RGB565: { ushort palettePixelData = (ushort)((Buffer.GetByte(paletteData, 2 * paletteIndex) << 8) | Buffer.GetByte(paletteData, 2 * paletteIndex + 1)); RGB565ToRGBA8(palettePixelData, ref dest, offset); } break; case GXTlutFmt.RGB5A3: { ushort palettePixelData = (ushort)((Buffer.GetByte(paletteData, 2 * paletteIndex) << 8) | Buffer.GetByte(paletteData, 2 * paletteIndex + 1)); RGB5A3ToRGBA8(palettePixelData, ref dest, offset); } break; } }
/// <summary> /// /// </summary> /// <param name="tobj"></param> /// <param name="bmp"></param> /// <param name="imgFormat"></param> /// <param name="palFormat"></param> public static void InjectBitmap(HSD_TOBJ tobj, Bitmap bmp, GXTexFmt imgFormat, GXTlutFmt palFormat) { if (imgFormat != GXTexFmt.CMP) { var bitmapData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), System.Drawing.Imaging.ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb); var length = bitmapData.Stride * bitmapData.Height; byte[] bytes = new byte[length]; Marshal.Copy(bitmapData.Scan0, bytes, 0, length); bmp.UnlockBits(bitmapData); tobj.EncodeImageData(bytes, bmp.Width, bmp.Height, imgFormat, palFormat); } else { MemoryStream stream = new MemoryStream(); bmp.Save(stream, System.Drawing.Imaging.ImageFormat.Png); byte[] bytes = stream.ToArray(); stream.Close(); stream.Dispose(); IntPtr unmanagedPointer = Marshal.AllocHGlobal(bytes.Length); Marshal.Copy(bytes, 0, unmanagedPointer, bytes.Length); using (var origImage = TexHelper.Instance.LoadFromWICMemory(unmanagedPointer, bytes.Length, WIC_FLAGS.NONE)) { var scratch = origImage.Compress(0, DXGI_FORMAT.BC1_UNORM, TEX_COMPRESS_FLAGS.DEFAULT, 1); var ptr = scratch.GetPixels(); var length = scratch.GetPixelsSize(); byte[] data = new byte[length]; Marshal.Copy(ptr, data, 0, (int)length); scratch.Dispose(); tobj.EncodeImageData(data, bmp.Width, bmp.Height, GXTexFmt.CMP, GXTlutFmt.IA8); } // Call unmanaged code Marshal.FreeHGlobal(unmanagedPointer); } }
public static byte[] EncodeTPL(byte[] rgba, int width, int height, GXTexFmt format, GXTlutFmt palformat, out byte[] paletteData) { return(EncodeTPL(Shared.ByteArrayToUIntArray(rgba), width, height, format, palformat, out paletteData)); }
/// <summary> /// /// </summary> /// <param name="rgba"></param> /// <param name="format"></param> /// <param name="palformat"></param> /// <param name="paletteData"></param> /// <returns></returns> public static byte[] EncodeTPL(uint[] rgba, int width, int height, GXTexFmt format, GXTlutFmt palformat, out byte[] paletteData) { paletteData = null; if (IsPalettedFormat(format)) { return(FromImage(rgba, width, height, format, palformat, out paletteData)); } else { return(FromImage(rgba, width, height, format)); } }
/// <summary> /// /// </summary> /// <param name="tobj"></param> /// <param name="bmp"></param> /// <param name="imgFormat"></param> /// <param name="palFormat"></param> public static void InjectBitmap(Bitmap bmp, HSD_TOBJ tobj, GXTexFmt imgFormat, GXTlutFmt palFormat) { if (imgFormat == GXTexFmt.CI8) // doesn't work well with alpha { bmp = BitmapTools.ReduceColors(bmp, 256); } if (imgFormat == GXTexFmt.CI4 || imgFormat == GXTexFmt.CI14X2) { bmp = BitmapTools.ReduceColors(bmp, 16); } var bitmapData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), System.Drawing.Imaging.ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb); var length = bitmapData.Stride * bitmapData.Height; byte[] bytes = new byte[length]; Marshal.Copy(bitmapData.Scan0, bytes, 0, length); bmp.UnlockBits(bitmapData); tobj.EncodeImageData(bytes, bmp.Width, bmp.Height, imgFormat, palFormat); // dispose if we use our color reduced bitmap //if (imgFormat == GXTexFmt.CI8 || imgFormat == GXTexFmt.CI4 || imgFormat == GXTexFmt.CI14X2) // bmp.Dispose(); }
/// <summary> /// /// </summary> /// <returns></returns> private static Tuple <byte[], ushort[]> EncodeC8(byte[] rgba, int width, int height, GXTlutFmt paletteFormat) { List <Color32> palColors = new List <Color32>(); uint numBlocksW = (uint)width / 8; uint numBlocksH = (uint)height / 4; byte[] pixIndices = new byte[numBlocksH * numBlocksW * 8 * 4]; for (int i = 0; i < (width * height) * 4; i += 4) { palColors.Add(new Color32(rgba[i + 2], rgba[i + 1], rgba[i + 0], rgba[i + 3])); } SortedList <ushort, Color32> rawColorData = new SortedList <ushort, Color32>(); foreach (Color32 col in palColors) { EncodeColor(paletteFormat, col, rawColorData); } int pixIndex = 0; for (int yBlock = 0; yBlock < numBlocksH; yBlock++) { for (int xBlock = 0; xBlock < numBlocksW; xBlock++) { for (int pY = 0; pY < 4; pY++) { for (int pX = 0; pX < 8; pX++) { pixIndices[pixIndex++] = (byte)rawColorData.IndexOfValue(palColors[width * ((yBlock * 4) + pY) + (xBlock * 8) + pX]); } } } } //PaletteCount = (ushort)rawColorData.Count; return(new Tuple <byte[], ushort[]>(pixIndices, rawColorData.Keys.ToArray())); }