/// <summary>Blank constructor</summary> public Panl(bool forLfd) { _type = ResourceType.Panl; if (forLfd) { _images = new Bitmap[1]; } else { _images = new Bitmap[104]; } _imageIndexer = new ImageIndexer(this); }
public int getClosestColorWithAlpha(Color c, Color[] pal) { int bestInd = 0; float bestDif = ImageIndexer.colorDifference(pal[0], c); for (int i = 0; i < pal.Length; i++) { float d = ImageIndexer.colorDifference(pal[i], c); if (d < bestDif) { bestDif = d; bestInd = i; } } return(bestInd); }
void _read(FileStream stream, long filePosition) { try { _fileName = stream.Name; // Resource._fileName: even though _process gets it, _isPnl needs it first if (!_isPnl) { _process(stream, filePosition); } else { BinaryReader br = new BinaryReader(stream); _offset = filePosition; // Resource._offset _type = ResourceType.Panl; // *.PNL files do not contain headers, just the raw data _name = StringFunctions.GetFileName(_fileName); DecodeResource(br.ReadBytes((int)stream.Length), false); } } catch (Exception x) { throw new LoadFileException(x); } _imageIndexer = new ImageIndexer(this); }
static private int getClosestColor(Color[] pal, Color c) { if (c.A == 0) { return(0); } int bestInd = 1; float bestDif = ImageIndexer.colorDifferenceWithoutAlpha(pal[1], c); for (int i = 1; i < pal.Length; i++) { float d = ImageIndexer.colorDifferenceWithoutAlpha(pal[i], c); if (d < bestDif) { bestDif = d; bestInd = i; } } return(bestInd); }
public float palDifUni(Color[] a, Color[] b) { bool aTransp = a[3] == Color.Transparent; bool bTransp = b[3] == Color.Transparent; if (aTransp != bTransp) { return(float.PositiveInfinity); } float dif = 0; int len = aTransp ? 3 : 4; bool[] sel = new bool[len]; for (int i = 0; i < len; i++) { Color c = a[i]; float diff = float.PositiveInfinity; int i2 = -1; for (int j = 0; j < len; j++) { if (sel[j]) { continue; } float diff2 = ImageIndexer.colorDifference(c, b[j]); if (diff2 < diff || i2 == -1) { i2 = j; diff = diff2; } } sel[i2] = true; dif += diff; } return(dif); }
public Color[] palMerge(Color[] a, Color[] b) { //return a; //FIXME!!!! //Very ugly hack here. I put the 8 colors in a bitmap //and let ImageIndexer find me a good 4-color palette :P bool trans = false; Bitmap bi = new Bitmap(8, 1); LockBitmap iii = new LockBitmap(bi); iii.LockBits(); for (int i = 0; i < 4; i++) { iii.SetPixel(i, 0, a[i]); iii.SetPixel(i + 4, 0, b[i]); if (b[i] == Color.Transparent || a[i] == Color.Transparent) { trans = true; } } iii.UnlockBits(); List <Color> pal1 = new List <Color>(); //if (!color2) //{ pal1.AddRange(ImageIndexer.createPaletteForImage(bi, 4, trans)); //(trans ? 3 : 4), false)); //} ///else //{ // pal1.AddRange(ImageIndexer.createPaletteForImage(bi, 2, trans));//(trans ? 3 : 4), false)); // pal1.AddRange(new Color[2]); //} //if (trans) { pal1.Add(Color.Transparent); } Color[] pal = pal1.ToArray(); transparentToTheEnd(pal); return(pal); //Haha, it was too slow :) /*Color[] pal = new Color[4]; * * int one = 0; * int two = 0; * for (int i = 0; i < a.Length; i++) * { * * int tdiff = (b[i].R - a[i].R) + (b[i].G - a[i].G) + (b[i].B - a[i].B); * if (tdiff == 0) * one++; * else if (tdiff < 0) * two++; * else * { * * two++; * } * * } * return one >= two ? a : b;*/ }
/*public ImageTexeler(Bitmap img, int paletteMaxNum) * { * this.img = img; * * int tx = img.Width / 4; * int ty = img.Height / 4; * palettes = new Color[tx * ty][]; * paletteCounts = new int[tx * ty]; * paletteNumbers = new int[tx, ty]; * paletteDiffs = new float[tx * ty, tx * ty]; * * int palNum = 0; * for (int x = 0; x < tx; x++) * for (int y = 0; y < ty; y++) * { * ImageIndexerFast iif = new ImageIndexerFast(img, x * 4, y * 4); * palettes[palNum] = iif.palette; * paletteNumbers[x, y] = palNum; * paletteCounts[palNum] = 1; * int similar = calcPaletteDiffs(palNum); * /* if (similar != -1) * { * paletteCounts[palNum] = 0; * paletteCounts[similar]++; * paletteNumbers[x, y] = similar; * } * * palNum++; * } * * while (countUsedPalettes() > paletteMaxNum) * { * Console.Out.WriteLine(countUsedPalettes()); * int besta = -1; * int bestb = -1; * float bestDif = float.MaxValue; * * * //Find the two most similar palettes * for (int i = 0; i < palettes.Length; i++) * { * if (paletteCounts[i] == 0) continue; * for (int j = 0; j < palettes.Length; j++) * { * if (i == j) continue; * if (paletteCounts[j] == 0) continue; * * if (paletteDiffs[i, j] < bestDif) * { * bestDif = paletteDiffs[i, j]; * besta = j; * bestb = i; * } * } * } * * //Merge the Palettes!!! * palettes[besta] = palMerge(palettes[besta], palettes[bestb]); * calcPaletteDiffs(besta); * paletteCounts[besta] += paletteCounts[bestb]; * paletteCounts[bestb] = 0; * * for (int x = 0; x < tx; x++) * for (int y = 0; y < ty; y++) * if (paletteNumbers[x, y] == bestb) * paletteNumbers[x, y] = besta; * } * * * * //CREATE THE FINAL PAL * int currNum = 0; * finalPalette = new Color[paletteMaxNum * 4]; * int[] newPalNums = new int[palettes.Length]; * for (int i = 0; i < palettes.Length; i++) * { * if (paletteCounts[i] != 0) * { * transparentToTheEnd(palettes[i]);// * newPalNums[i] = currNum; * Array.Copy(palettes[i], 0, finalPalette, currNum * 4, 4); * currNum++; * } * } * * ByteArrayOutputStream texDat = new ByteArrayOutputStream(); * ByteArrayOutputStream f5Dat = new ByteArrayOutputStream(); * for (int y = 0; y < ty; y++) * for (int x = 0; x < tx; x++) * { * //Find out if texel has transparent. * * bool hasTransparent = false; * for (int yy = 0; yy < 4; yy++) * for (int xx = 0; xx < 4; xx++) * { * Color coll = img.GetPixel(x * 4 + xx, y * 4 + yy); * if (coll.A < 128) * hasTransparent = true; * } * * //WRITE THE IMAGE DATA * for (int yy = 0; yy < 4; yy++) * { * byte b = 0; * byte pow = 1; * for (int xx = 0; xx < 4; xx++) * { * Color coll = img.GetPixel(x * 4 + xx, y * 4 + yy); * byte col; * if (coll.A < 128) * { * col = 3; * } * else * { * col = (byte)ImageIndexer.closest(coll, palettes[paletteNumbers[x, y]]); * if (col == 3) { col = 2; } * } * b |= (byte)(pow * col); * pow *= 4; * } * texDat.writeByte(b); * } * * * //WRITE THE FORMAT-5 SPECIFIC DATA * ushort dat = (ushort)(newPalNums[paletteNumbers[x, y]] * 2); * if (!hasTransparent || !ContainsTransparent(img)) * { * dat |= 2 << 14; * } * f5Dat.writeUShort(dat); * } * * f5data = f5Dat.getArray(); * texdata = texDat.getArray(); * * }*/ public ImageTexeler(Bitmap img, int paletteMaxNum, ref System.ComponentModel.BackgroundWorker bw, bool color2 = false) { this.color2 = false; color2 = false; //this.color2 = color2; //bool trans = true;//ContainsTransparent(img); Bitmap im = new Bitmap(img.Width, img.Height, System.Drawing.Imaging.PixelFormat.Format64bppPArgb); using (Graphics gr = Graphics.FromImage(im)) { gr.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.None; gr.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor; gr.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighQuality; gr.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality; gr.DrawImage(img, 0, 0); } this.img = im; LockBitmap iii = new LockBitmap(img); iii.LockBits(); int tx = img.Width / 4; int ty = img.Height / 4; palettes = new List <Color[]>(); //[tx * ty][]; paletteCounts = new List <int>(); //new int[tx * ty]; paletteNumbers = new int[tx, ty]; paletteDiffs = new float[tx * ty, tx *ty]; double add = 18d / (double)(tx * ty); double Progress = 10; int palNum = 0; double percent = 0; double add2 = 100d / (double)(tx * ty); for (int x = 0; x < tx; x++) { for (int y = 0; y < ty; y++) { Bitmap ni = new Bitmap(4, 4 /*, System.Drawing.Imaging.PixelFormat.Format16bppRgb555*/); /*using (Graphics gr = Graphics.FromImage(ni)) * { * //gr.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality; * //gr.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic; * //gr.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighQuality; * //gr.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality; * gr.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.None;//.AntiAlias; * gr.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor; * gr.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighQuality; * gr.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality; * * gr.DrawImage(this.img, new Rectangle(0, 0, 4, 4), new Rectangle(x * 4, y * 4, 4, 4), GraphicsUnit.Pixel); * }*/ LockBitmap nn = new LockBitmap(ni); nn.LockBits(); bool haveTransparent = false; for (int x1 = 0; x1 < 4; x1++) { for (int y1 = 0; y1 < 4; y1++) { Color c = iii.GetPixel(x * 4 + x1, y * 4 + y1); nn.SetPixel(x1, y1, c); if (c.A < 128) { haveTransparent = true; //goto end; } } } //end: nn.UnlockBits(); List <Color> pal1 = new List <Color>(); pal1.AddRange(ImageIndexer.createPaletteForImage(ni, 4, haveTransparent)); //if(haveTransparent){pal1.Add(Color.Transparent);} Color[] pal = pal1.ToArray(); //ImageIndexer.createPaletteForImage(ni, 4, false); transparentToTheEnd(pal); //if (haveTransparent) //{ // pal[0] = Color.Transparent; //} //ImageIndexerFast iif = new ImageIndexerFast(img, x * 4, y * 4); int con = contains(palettes.ToArray(), pal); if (con != -1) { paletteNumbers[x, y] = con; //paletteCounts.Add(1); } else { palettes.Add(pal); //[palNum] = pal;//iif.palette; paletteNumbers[x, y] = palettes.Count - 1; paletteCounts.Add(1); //[palNum] = 1; calcPaletteDiffs(palettes.Count - 1); } //int similar = calcPaletteDiffs(palNum); // if (similar != -1) // { // paletteCounts[palNum] = 0; // paletteCounts[similar]++; // paletteNumbers[x, y] = similar; // } palNum++; Progress += add; bw.ReportProgress((int)Progress, "Generating Picture " + percent.ToString("000") + "%"); percent += add2; if (bw.CancellationPending) { bw.ReportProgress(0, "Canceled"); return; } } } percent = 0; add2 = 100d / (((double)countUsedPalettes() - (double)paletteMaxNum)); add = 74d / (((double)countUsedPalettes() - (double)paletteMaxNum)); //double iw = 0; while (countUsedPalettes() > paletteMaxNum) { //iw += 1; //Console.Out.WriteLine(countUsedPalettes()); int besta = -1; int bestb = -1; float bestDif = float.MaxValue; //Find the two most similar palettes for (int i = 0; i < palettes.Count; i++) { if (paletteCounts[i] == 0) { continue; } for (int j = 0; j < palettes.Count; j++) { if (i == j) { continue; } if (paletteCounts[j] == 0) { continue; } if (paletteDiffs[i, j] < bestDif) { bestDif = paletteDiffs[i, j]; besta = j; bestb = i; } } } //Merge the Palettes!!! palettes[besta] = palMerge(palettes[besta], palettes[bestb]); calcPaletteDiffs(besta); paletteCounts[besta] += paletteCounts[bestb]; paletteCounts[bestb] = 0; for (int x = 0; x < tx; x++) { for (int y = 0; y < ty; y++) { if (paletteNumbers[x, y] == bestb) { paletteNumbers[x, y] = besta; } } } Progress += add; percent += add2; bw.ReportProgress((int)Progress, "Generating Palette " + percent.ToString("000") + "%"); if (bw.CancellationPending) { bw.ReportProgress(0, "Canceled"); return; } } //CREATE THE FINAL PAL int currNum = 0; finalPalette = new Color[countUsedPalettes() * 4]; int[] newPalNums = new int[palettes.Count]; for (int i = 0; i < palettes.Count; i++) { if (paletteCounts[i] != 0) { transparentToTheEnd(palettes[i]);// newPalNums[i] = currNum; Array.Copy(palettes[i], 0, finalPalette, currNum * 4, 4); currNum++; } } ByteArrayOutputStream texDat = new ByteArrayOutputStream(); ByteArrayOutputStream f5Dat = new ByteArrayOutputStream(); for (int y = 0; y < ty; y++) { for (int x = 0; x < tx; x++) { //Find out if texel has transparent. bool hasTransparent = false; for (int yy = 0; yy < 4; yy++) { for (int xx = 0; xx < 4; xx++) { Color coll = iii.GetPixel(x * 4 + xx, y * 4 + yy); if (coll.A < 128) { hasTransparent = true; goto End; } } } End: //WRITE THE IMAGE DATA for (int yy = 0; yy < 4; yy++) { byte b = 0; byte pow = 1; for (int xx = 0; xx < 4; xx++) { Color coll = iii.GetPixel(x * 4 + xx, y * 4 + yy); byte col; if (coll.A < 128) { col = 3; } else { List <Color> colo = new List <Color>(); colo.AddRange(palettes[paletteNumbers[x, y]]); if (hasTransparent) { colo.RemoveAt(3); } col = (byte)ImageIndexer.closest(coll, colo.ToArray()); //if (col == 3) { col = 2; } } b |= (byte)(pow * col); pow *= 4; } texDat.writeByte(b); } //WRITE THE FORMAT-5 SPECIFIC DATA ushort dat = (ushort)(newPalNums[paletteNumbers[x, y]] * 2); if (!hasTransparent /* || !ContainsTransparent(img)*/) { dat |= 2 << 14; } f5Dat.writeUShort(dat); } } iii.UnlockBits(); f5data = f5Dat.getArray(); texdata = texDat.getArray(); }
/// <summary>Blank constructor</summary> public Panl(bool forLfd) { _type = ResourceType.Panl; if (forLfd) _images = new Bitmap[1]; else _images = new Bitmap[104]; _imageIndexer = new ImageIndexer(this); }
void _read(FileStream stream, long filePosition) { try { _fileName = stream.Name; // Resource._fileName: even though _process gets it, _isPnl needs it first if (!_isPnl) _process(stream, filePosition); else { BinaryReader br = new BinaryReader(stream); _offset = filePosition; // Resource._offset _type = ResourceType.Panl; // *.PNL files do not contain headers, just the raw data _name = StringFunctions.GetFileName(_fileName); DecodeResource(br.ReadBytes((int)stream.Length), false); } } catch (Exception x) { throw new LoadFileException(x); } _imageIndexer = new ImageIndexer(this); }
static void Main(string[] args) { Console.WriteLine("Image to ENPG Batch Encoder - v1.0"); Console.WriteLine("By Mariomaster using NSMBe's Image Indexer and LZ77 compressor"); Console.WriteLine(); if (args.Length == 0) { Console.WriteLine("Drag 256x256 Image files onto this application to convert them to NSMB's ENPG format."); Console.WriteLine(); Console.Write("Press any key to exit..."); Console.ReadKey(); return; } bool compress = false; bool forCredits = false; checkCompress: Console.Write("Do you want to LZ77 compress the ENPGs? (y/n): "); char rc = Console.ReadKey().KeyChar; Console.WriteLine(); if (rc == 'y' || rc == 'Y') { compress = true; } else if (rc == 'n' || rc == 'N') { compress = false; } else { goto checkCompress; } checkForCredits: Console.Write("Are the Images intended as Credit Images (Reserve first 5 Palette slots)? (y/n): "); char rcr = Console.ReadKey().KeyChar; Console.WriteLine(); if (rcr == 'y' || rcr == 'Y') { forCredits = true; } else if (rcr == 'n' || rcr == 'N') { forCredits = false; } else { goto checkForCredits; } Console.WriteLine(); Console.WriteLine("Encoding " + args.Length + " images."); Console.WriteLine(); int successCount = 0; foreach (string path in args) { if (!File.Exists(path)) { Console.WriteLine("The file " + path + " does not exist!"); continue; } if (!System.Web.MimeMapping.GetMimeMapping(path).StartsWith("image/")) { Console.WriteLine("The file " + path + " is not an Image!"); continue; } Bitmap bmp = (Bitmap)Image.FromFile(path); if (bmp.Width != 256 || bmp.Height != 256) { Console.WriteLine("The Image " + path + " is not 256x256!"); continue; } string folder = Path.GetDirectoryName(path); string name = Path.GetFileNameWithoutExtension(path); Console.Write("Encoding " + path + "..."); bmp.RotateFlip(RotateFlipType.Rotate90FlipX); int cCount = 256; if (forCredits) { cCount = 251; } Color[] pal = ImageIndexer.createPaletteForImage(bmp, cCount); ByteArrayOutputStream b = new ByteArrayOutputStream(); for (int x = 0; x < bmp.Width; x++) { for (int y = 0; y < bmp.Height; y++) { Color c = bmp.GetPixel(x, y); int i = getClosestColor(pal, c); int writeColor = i; if (forCredits) { if (writeColor > 0) { writeColor += 5; } } b.writeByte((byte)writeColor); } } b.writeUShort(toRGB15(pal[0])); if (forCredits) { for (int i = 0; i < 5; i++) { b.writeUShort(0x0); } } for (int i = 1; i < pal.Length; i++) { b.writeUShort(toRGB15(pal[i])); } if (!Directory.Exists(folder + "\\convert")) { Directory.CreateDirectory(folder + "\\convert"); } var bw = new BinaryWriter(File.Open(folder + "\\convert\\" + name + ".enpg", FileMode.OpenOrCreate)); if (compress) { bw.Write(lz77.LZ77_Compress(b.getArray())); } else { bw.Write(b.getArray()); } bw.Flush(); bw.Close(); successCount++; Console.WriteLine("\rEncoding " + path + " done."); } Console.WriteLine(); Console.Write("Successfully encoded " + successCount + " of " + args.Length + " images. Press any key to exit..."); Console.ReadKey(); }
public ImageTexeler(Bitmap img, int paletteMaxNum) { this.img = img; int tx = img.Width / 4; int ty = img.Height / 4; palettes = new Color[tx * ty][]; paletteCounts = new int[tx * ty]; paletteNumbers = new int[tx, ty]; paletteDiffs = new float[tx * ty, tx *ty]; int palNum = 0; for (int x = 0; x < tx; x++) { for (int y = 0; y < ty; y++) { ImageIndexerFast iif = new ImageIndexerFast(img, x * 4, y * 4); palettes[palNum] = iif.palette; paletteNumbers[x, y] = palNum; paletteCounts[palNum] = 1; int similar = calcPaletteDiffs(palNum); /* if (similar != -1) * { * paletteCounts[palNum] = 0; * paletteCounts[similar]++; * paletteNumbers[x, y] = similar; * } */ palNum++; } } while (countUsedPalettes() > paletteMaxNum) { Console.Out.WriteLine(countUsedPalettes()); int besta = -1; int bestb = -1; float bestDif = float.MaxValue; //Find the two most similar palettes for (int i = 0; i < palettes.Length; i++) { if (paletteCounts[i] == 0) { continue; } for (int j = 0; j < palettes.Length; j++) { if (i == j) { continue; } if (paletteCounts[j] == 0) { continue; } if (paletteDiffs[i, j] < bestDif) { bestDif = paletteDiffs[i, j]; besta = j; bestb = i; } } } //Merge the Palettes!!! palettes[besta] = palMerge(palettes[besta], palettes[bestb]); calcPaletteDiffs(besta); paletteCounts[besta] += paletteCounts[bestb]; paletteCounts[bestb] = 0; for (int x = 0; x < tx; x++) { for (int y = 0; y < ty; y++) { if (paletteNumbers[x, y] == bestb) { paletteNumbers[x, y] = besta; } } } } //CREATE THE FINAL PAL int currNum = 0; finalPalette = new Color[paletteMaxNum * 4]; int[] newPalNums = new int[palettes.Length]; for (int i = 0; i < palettes.Length; i++) { if (paletteCounts[i] != 0) { //transparentToTheEnd(palettes[i]); newPalNums[i] = currNum; Array.Copy(palettes[i], 0, finalPalette, currNum * 4, 4); currNum++; } } ByteArrayOutputStream texDat = new ByteArrayOutputStream(); ByteArrayOutputStream f5Dat = new ByteArrayOutputStream(); for (int y = 0; y < ty; y++) { for (int x = 0; x < tx; x++) { //Find out if texel has transparent. bool hasTransparent = false; for (int yy = 0; yy < 4; yy++) { for (int xx = 0; xx < 4; xx++) { Color coll = img.GetPixel(x * 4 + xx, y * 4 + yy); if (coll.A < 128) { hasTransparent = true; } } } //WRITE THE IMAGE DATA for (int yy = 0; yy < 4; yy++) { byte b = 0; byte pow = 1; for (int xx = 0; xx < 4; xx++) { Color coll = img.GetPixel(x * 4 + xx, y * 4 + yy); byte col; if (coll.A < 128) { col = 3; } else { col = (byte)ImageIndexer.closest(coll, palettes[paletteNumbers[x, y]]); if (col == 3) { col = 2; } } b |= (byte)(pow * col); pow *= 4; } texDat.writeByte(b); } //WRITE THE FORMAT-5 SPECIFIC DATA ushort dat = (ushort)(newPalNums[paletteNumbers[x, y]] * 2); if (!hasTransparent) { dat |= 2 << 14; } f5Dat.writeUShort(dat); } } f5data = f5Dat.getArray(); texdata = texDat.getArray(); }