Ejemplo n.º 1
0
        /// <summary>
        /// Automatically generates a TSA-compliant image from a 256(or fewer)-color image
        /// </summary>
        /// <param name="width">Width of the TSA (in tiles)</param>
        /// <param name="height">Height of the TSA (in tiles)</param>
        /// <param name="image">The bitmap to convert</param>
        /// <param name="paletteAmount">the maximum amount of 16-color palettes</param>
        /// <param name="checkRedundantTiles">whether or not to add all tiles found to the tileset</param>
        public TSA_Image(
            int width, int height,
            GBA.Bitmap image,
            int paletteAmount,
            bool checkRedundantTiles)
        {
            Width  = width * 8;
            Height = height * 8;

            if (image.Width != Width || image.Height != Height)
            {
                throw new Exception("Image given has invalid dimensions.\n" +
                                    "It must be " + Width + "x" + Height + " pixels.");
            }

            if (image.Colors.Count <= 16)
            {   // No need to run TSA-ifier code
                Palettes = Palette.Split(image.Colors, paletteAmount);

                Graphics = new Tileset(new Image(image));

                Tiling = TSA_Array.GetBasicTSA(width, height);
            }
            else
            {
                using (FormLoading loading = new FormLoading())
                { // Run the image TSA-ifier
                    loading.Show();

                    int    tileAmount  = width * height;
                    byte[] bytes       = image.ToBytes();
                    int[]  colorTotals = new int[image.Colors.Count];
                    int[,] colorAmounts = new int[image.Colors.Count, tileAmount];

                    loading.SetLoading("Checking colors...", 2);

                    int index;
                    int tile = 0;
                    for (int tileY = 0; tileY < height; tileY++)
                    {
                        for (int tileX = 0; tileX < width; tileX++)
                        {
                            index = tileX * 8 + tileY * 8 * Width;
                            for (int y = 0; y < 8; y++)
                            {
                                for (int x = 0; x < 8; x++)
                                {
                                    colorTotals[bytes[index]]++;
                                    colorAmounts[bytes[index], tile]++;
                                }
                                index += Width - 8;
                            }
                            tile++;
                        } // first we take a look at which colors are most present (in all, and per tile)
                    }
                    loading.SetPercent(7);

                    List <int> colors = new List <int>();
                    for (int i = 0; i < colorTotals.Length; i++)
                    {
                        index = 0;
                        while (index < colors.Count && colorTotals[i] > colors[index])
                        {
                            index++;
                        }

                        if (index < colors.Count)
                        {
                            colors.Insert(index, i);
                        }
                        else
                        {
                            colors.Add(i);
                        }

                        if (colors.Count > paletteAmount * 15)
                        {
                            colors.RemoveAt(0);
                        }
                    } // then we create a list of indices to colors with only the most used colors

                    loading.SetPercent(9);

                    Palettes = new Palette[paletteAmount];
                    for (int i = 0; i < paletteAmount; i++)
                    {
                        Palettes[i] = new GBA.Palette(16);
                        Palettes[i].Add(new GBA.Color(0x0000));
                    } // we create our palettes, forcing the 1st color to be black on each palette

                    loading.SetLoading("Asserting tile palettes...", 10);

                    byte[]    tilePalettes = new byte[tileAmount];
                    int[]     certainty    = new int[tileAmount];
                    GBA.Color color;
                    int       amount;
                    for (int i = colors.Count - 1; i >= 0; i--)
                    {
                        color = image.Colors[colors[i]];
                        tile  = 0;

                        while (tile < tilePalettes.Length)
                        {
                            amount = colorAmounts[colors[i], tile];

                            if (Palettes[tilePalettes[tile]].Contains(color))
                            {
                                certainty[tile] += amount;
                            }
                            else
                            {
                                if (Palettes[tilePalettes[tile]].IsFull)
                                {
                                    if (certainty[tile] < amount)
                                    {
                                        tilePalettes[tile]++;
                                        tilePalettes[tile] %= (byte)Palettes.Length;
                                        certainty[tile]     = 0;
                                        i++; break;
                                    }
                                    else
                                    {
                                        certainty[tile] -= amount;
                                    }
                                }
                                else if (amount != 0)
                                {
                                    Palettes[tilePalettes[tile]].Add(color);
                                    certainty[tile] += amount;
                                }
                            }
                            tile++;
                        }
                        loading.SetPercent(10 + (50 - 50 * ((float)i / (float)(colors.Count))));
                    } // and we do a loop going from  most used color to least used, setting tilePalettes and filling said palettes

                    amount = 0;
                    for (int p = 0; p < Palettes.Length; p++)
                    {
                        Palettes[p].Sort(delegate(GBA.Color first, GBA.Color second)
                        {
                            return((first.GetValueR() + first.GetValueG() + first.GetValueB())
                                   - (second.GetValueR() + second.GetValueG() + second.GetValueB()));
                        });
                        for (int i = Palettes[p].Count; i < 16; i++)
                        {
                            Palettes[p].Add(new Color(0x0000));
                        }
                    }

                    loading.SetMessage("Creating TSA information...");

                    Graphics = new Tileset(width * height);
                    Tiling   = new TSA_Array(width, height);
                    int pixel;
                    tile = 0;
                    byte HI_nibble;
                    byte LO_nibble;
                    Tile currentTile;
                    Tuple <int, bool, bool> current;
                    for (int tileY = 0; tileY < height; tileY++)
                    {
                        for (int tileX = 0; tileX < width; tileX++)
                        {
                            index = (Palettes[tilePalettes[tile]].IsFull) ? tilePalettes[tile] : 0;
                            bytes = new byte[GBA.Tile.LENGTH];

                            for (int y = 0; y < 8; y++)
                            {
                                for (int x = 0; x < 4; x++)
                                {
                                    color     = image.GetColor(tileX * 8 + x * 2, tileY * 8 + y);
                                    pixel     = GBA.Color.GetNearest(Palettes[index], color);
                                    LO_nibble = (pixel == -1) ? (byte)0x00 : (byte)(pixel);

                                    color     = image.GetColor(tileX * 8 + x * 2 + 1, tileY * 8 + y);
                                    pixel     = GBA.Color.GetNearest(Palettes[index], color);
                                    HI_nibble = (pixel == -1) ? (byte)0x00 : (byte)(pixel);

                                    bytes[x + y * 4] = (byte)((HI_nibble << 4) | LO_nibble);
                                }
                            }

                            currentTile = new Tile(bytes);

                            if (checkRedundantTiles)
                            {
                                if (currentTile.IsEmpty())
                                {
                                    current = Tuple.Create(0, false, false);
                                }
                                else
                                {
                                    current = Graphics.FindMatch(currentTile);

                                    if (current == null)
                                    {
                                        current = Tuple.Create(Graphics.Count, false, false);
                                        Graphics.Add(currentTile);
                                    }
                                }
                            }
                            else
                            {
                                current = Tuple.Create(Graphics.Count, false, false);
                                Graphics.Add(currentTile);
                            }
                            // try {
                            Tiling[tileX, tileY] = new TSA(
                                (UInt16)current.Item1,
                                tilePalettes[tile],
                                current.Item2,
                                current.Item3);
                            // } catch { }

                            loading.SetPercent(60 + 40 * ((float)tile / (float)tileAmount));
                            tile++;
                        }
                    }
                }
            }
        }
Ejemplo n.º 2
0
        /// <summary>
        /// Creates a Palette from a palette file (if its an image file, must be 16 pixels in width)
        /// </summary>
        public Palette(string filepath, int maximum = MAX)
        {
            Colors = new List <Color>(maximum);

            if (filepath.EndsWith(".png", StringComparison.OrdinalIgnoreCase) ||
                filepath.EndsWith(".gif", StringComparison.OrdinalIgnoreCase) ||
                filepath.EndsWith(".bmp", StringComparison.OrdinalIgnoreCase))
            {
                Bitmap source;
                try
                {
                    source = new GBA.Bitmap(filepath);
                }
                catch { throw new Exception("Could not open palette file:\n" + filepath); }

                bool  addEveryPixel = (source.Width == 16);
                int   index         = 0;
                Color color;
                for (int y = 0; y < source.Height; y++)
                {
                    for (int x = 0; x < source.Width; x++)
                    {
                        color = source.GetColor(x, y);
                        if (!addEveryPixel && this.Contains(color))
                        {
                            continue;
                        }
                        else
                        {
                            if (this.IsFull)
                            {
                                throw new Exception("This palette cannot hold more than " + maximum + " colors.");
                            }
                            else
                            {
                                this.Colors.Add(color);
                                index++;
                            }
                        }
                    }
                }
            }
            else if (filepath.EndsWith(".pal", StringComparison.OrdinalIgnoreCase))
            {
                byte[] palette = File.ReadAllBytes(filepath);

                if (palette[0] == 0x43 &&
                    palette[1] == 0x4C &&
                    palette[2] == 0x52 &&
                    palette[3] == 0x58) // 'CLRX' is the header for usenti .pal files
                {
                    string[] file = File.ReadAllLines(filepath);
                    int      channelBits;
                    int      colorAmount;

                    int parse  = 5;
                    int length = 0;

                    while (parse + length < file[0].Length && file[0][parse + length] != ' ')
                    {
                        length++;
                    }
                    channelBits = int.Parse(file[0].Substring(parse, length));

                    parse += length + 1;
                    length = 0;

                    while (parse + length < file[0].Length && file[0][parse + length] != ' ')
                    {
                        length++;
                    }
                    colorAmount = int.Parse(file[0].Substring(parse, length));

                    uint    color  = 0x00000000;
                    Palette colors = new Palette(colorAmount);
                    for (int i = 0; i < (colorAmount / 4); i++)
                    {
                        parse  = 0;
                        length = 0;

                        for (int j = 0; j < 4; j++)
                        {
                            while (parse + length < file[1 + i].Length &&
                                   file[1 + i][parse + length] != ' ')
                            {
                                length++;
                            }

                            color = Util.HexToInt(file[1 + i].Substring(parse, length));
                            colors.Add(new Color(color));

                            parse += length + 1;
                            length = 0;
                        }
                    }
                    palette = colors.ToBytes(false);
                }
                else if (palette.Length != maximum * 2)
                {
                    throw new Exception(
                              "Cannot load palette, the file given is of invalid length.\n" +
                              "It should be " + maximum * 2 + " bytes long.");
                }


                byte[] buffer = new byte[2];
                for (int i = 0; i < palette.Length / 2; i++)
                {
                    buffer[0] = palette[i * 2 + 1];
                    buffer[1] = palette[i * 2];
                    Colors.Add(new Color(buffer));
                }
            }
            else
            {
                throw new Exception("Invalid filetype given to load palette.");
            }
        }