Click here to Skip to main content
15,893,508 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
I have some large sprite sheets (up to 2048 x 2048 pixels, png 32 bit) which I use in my programs.

Now, I have the following options:
1. Split the image in an image editor and add the individual sprites to my program as resources (I don't like this approach)
2. Load the image, split it, keep the parts and then dispose it. (I like this most, as the job is done only once)
3. Load the image, keep it and get the sprites out of it when needed.

As for the actual splitting, I thought of two options:
1. Split it first in rows and then the rows in individual cells using ImageLists.
C#
private static ImageList Split(Bitmap image, int width, int height)
{
    ImageList rows = new ImageList();
    rows.ImageSize = new Size(image.Width, height);
    rows.Images.AddStrip(image);
    ImageList cells = new ImageList();
    cells.ImageSize = new Size(width, height);
    foreach (Image row in Rows.Images)
    {
        cells.Images.AddStrip(row);
    }
    return cells;
}

(rough code, just for purpose of exemplification)


2. Draw portions of it on a canvas using Graphics.
C#
private static ImageList Split(Bitmap image, int width, int height)
{
    ImageList cells = new ImageList();
    cells.ImageSize = new Size(width, height);
    Bitmap bmp = new Bitmap(width, height);
    Graphics g = Graphics.FromImage(bmp);
    for (int i = 0; i < image.Width / width; i++)
    {
        for (int j = 0; i < image.Height / height; j++)
        {
            g.DrawImageUnscaled(image, -i * width, -j * height);
            cells.Images.Add((Bitmap)bmp.Clone());
        }
    }
    return cells;
}

(rough code, just for purpose of exemplification)


However, both methods are quite slow... :(
There must be a better way!
Can someone give me a few ideas?
Thank you.

If you think this is a good question, please vote it.
Posted
Updated 27-Sep-10 23:57pm
v2

Good opportunity to take on threads and work your machine's processor.

Split the image into rows (which should be relatively quick), then put those into a queue. Work on each row with individual threads. Add each image to a collection and persist the collection when all the items are completed in the queue.

Use the sync'd version of the queue, there's an article here[^].

Edit to say: I do really think this is a good example of something that is slow linearly, but would tick along quite nicely if you thread it. These are discrete, non-related tasks that take a while to do...in other words, ideal for threading.

Cheers.
 
Share this answer
 
v2
Comments
Toli Cuturicu 28-Sep-10 14:14pm    
I don't have a dual-core CPU... :-(
Well, you could alternatively go the lockbits route.

Bob Powell has a great (series of) article(s) on the topic(s of GDI, image processing and .NET). Lockbits is here[^].

This will require a lower level of processing (and understanding of the image) but should be much faster.

Cheers.
 
Share this answer
 
Comments
Toli Cuturicu 28-Sep-10 16:49pm    
Fine.
I found another way myself, using Bitmap.Clone(Rectangle rect):
C#
private static int Main(string[] args)
{
    if (args.Length < 3)
    {
        Console.WriteLine("Error 1: Not enough arguments. Usage: SplitImage filename cellwidth cellheight");
        return -1;
    }
    string filename = args[0];
    if (!File.Exists(filename))
    {
        Console.WriteLine("Error 2: File not found.");
        return -2;
    }
    ushort width;
    ushort height;
    if (!ushort.TryParse(args[1], out width))
    {
        Console.WriteLine("Error 3: Cannot parse cell width.");
        return -3;
    }
    if (!ushort.TryParse(args[2], out height))
    {
        Console.WriteLine("Error 4: Cannot parse cell height.");
        return -4;
    }
    Bitmap bmp = null;
    try
    {
        bmp = (Bitmap)Image.FromFile(filename);
    }
    catch
    {
        Console.WriteLine("Error 5: Cannot load image from file.");
        return -5;
    }
    if (width == 0)
        width = (ushort)bmp.Width;
    if (height == 0)
        height = (ushort)bmp.Height;
    int columns = bmp.Width / width;
    int rows = bmp.Height / height;
    int cells = columns * rows;
    if (cells == 0)
    {
        Console.WriteLine("Warning: Cannot split image in 0 cells.");
        return 0;
    }
    string dir;
    try
    {
        dir = Path.GetDirectoryName(filename) + Path.DirectorySeparatorChar + Path.GetFileNameWithoutExtension(filename);
        if (!Directory.Exists(dir))
            Directory.CreateDirectory(dir);
    }
    catch
    {
        Console.WriteLine("Error 6: Cannot create directory for cells.");
        return -6;
    }
    Console.WriteLine();
    Console.WriteLine("About to split image in {0} rows x {1} columns = {2} cells...", rows, columns, cells);
    Console.WriteLine();
    int cellpadding = (cells - 1).ToString().Length;
    int rowpadding = (rows - 1).ToString().Length;
    string cellfile;
    string cellpath;
    try
    {
        for (int row = 0; row < rows; row++)
        {
            Console.Write("Row " + row.ToString().PadLeft(rowpadding, ' ') + ":  ");
            for (int column = 0; column < columns; column++)
            {
                cellfile = (row * columns + column).ToString().PadLeft(cellpadding, '0');
                cellpath = dir + Path.DirectorySeparatorChar + cellfile + ".png";
                bmp.Clone(new Rectangle(column * width, row * height, width, height), bmp.PixelFormat).Save(cellpath, ImageFormat.Png);
                Console.Write(cellfile + "  ");
            }
            Console.WriteLine();
        }
        Console.WriteLine();
        Console.WriteLine("{0} files written to disk.", cells);
        Console.WriteLine();
    }
    catch (Exception ex)
    {
        Console.WriteLine("Error 7: " + ex.Message);
        return -7;
    }
    return cells;
}
 
Share this answer
 

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900