1
/* Reorganize (c) 2006 Bjørn Lindeijer
2
 * License: GPL, v2 or later
3
 */
4
5
import java.awt.Color;
6
import java.awt.Graphics;
7
import java.awt.Rectangle;
8
import java.awt.image.BufferedImage;
9
import java.util.Vector;
10
import java.io.IOException;
11
import java.io.File;
12
import javax.imageio.ImageIO;
13
14
/**
15
 * Tool to reorganize the headgears.
16
 */
17
public class Reorganize
18
{
19
    private static final int SPRITE_WIDTH = 60;
20
    private static final int SPRITE_HEIGHT = 60;
21
    private static final int FRAMES = 10;
22
    private static final int DIRECTIONS = 4;
23
24
    private static final int HAIR_COLORS = 10;
25
    private static final int HAIR_FRAMES = 9;
26
    private static final int HAIR_SPRITE_WIDTH = 40;
27
    private static final int HAIR_SPRITE_HEIGHT = 40;
28
29
    private static final int TRANSPARENT = new Color(255, 0, 255).getRGB();
30
31
    public static void main(String[] arg)
32
    {
33
        if (arg.length != 2) {
34
            System.out.println("Usage:\n   java Reorganize [source] [target]");
35
            return;
36
        }
37
38
        BufferedImage source = null;
39
        try {
40
            source = ImageIO.read(new File(arg[0]));
41
        } catch (IOException e) {
42
            System.out.println("Error while trying to read " + arg[0] + ".");
43
            e.printStackTrace();
44
            System.exit(1);
45
        }
46
47
        // Read the existing frames into a vector
48
        Vector<BufferedImage> spriteSet = gridCut(source,
49
                HAIR_SPRITE_WIDTH, HAIR_SPRITE_HEIGHT,
50
                HAIR_FRAMES, 1);
51
52
        // Determine minimal rectangle that can still contain the contents of
53
        // any frame
54
        /*
55
        Rectangle cropRect = minimumCropRect(spriteSet);
56
57
        if (cropRect == null) {
58
            System.out.println(
59
                    "Error: no optimal crop rect could be determined.");
60
            System.exit(1);
61
        }
62
63
        System.out.println(arg[0] + ": width=\"" +
64
                cropRect.width + "\" height=\"" + cropRect.height + "\"");
65
        */
66
67
        filterHeadgear(spriteSet);
68
69
        BufferedImage target = gridDraw(
70
                spriteSet,
71
                new Rectangle(0, 0, HAIR_SPRITE_WIDTH, HAIR_SPRITE_HEIGHT),
72
                HAIR_FRAMES - 4, 1);
73
74
        // Save the target image
75
        try {
76
            ImageIO.write(target, "png", new File(arg[1]));
77
        } catch (IOException e) {
78
            System.out.println("Error while trying to write " + arg[1] + ".");
79
            e.printStackTrace();
80
            System.exit(1);
81
        }
82
    }
83
84
    private static Vector<BufferedImage> gridCut(
85
            BufferedImage source,
86
            int width, int height, int xFrames, int yFrames)
87
    {
88
        Vector<BufferedImage> spriteSet = new Vector<BufferedImage>();
89
90
        for (int y = 0; y < yFrames; y++) {
91
            for (int x = 0; x < xFrames; x++) {
92
                BufferedImage sprite = source.getSubimage(
93
                        x * width,
94
                        y * height,
95
                        width,
96
                        height);
97
98
                spriteSet.add(sprite);
99
            }
100
        }
101
102
        return spriteSet;
103
    }
104
105
    private static BufferedImage gridDraw(Vector<BufferedImage> spriteSet,
106
            Rectangle cropRect, int xFrames, int yFrames)
107
    {
108
        // Create a new image
109
        BufferedImage target = new BufferedImage(
110
                xFrames * cropRect.width,
111
                yFrames * cropRect.height,
112
                BufferedImage.TYPE_INT_ARGB);
113
114
        // Draw the frames onto the target image
115
        Graphics g = target.getGraphics();
116
        for (int y = 0; y < yFrames; y++) {
117
            for (int x = 0; x < xFrames; x++) {
118
                g.drawImage(
119
                        spriteSet.get(x + xFrames * y).getSubimage(
120
                            cropRect.x,
121
                            cropRect.y,
122
                            cropRect.width,
123
                            cropRect.height),
124
                        x * cropRect.width,
125
                        y * cropRect.height,
126
                        null);
127
            }
128
        }
129
130
        return target;
131
    }
132
133
    private static Rectangle minimumCropRect(Vector<BufferedImage> spriteSet)
134
    {
135
        Rectangle cropRect = null;
136
137
        for (BufferedImage sprite : spriteSet) {
138
            Rectangle frameCropRect = determineCropRect(sprite);
139
140
            if (cropRect == null) {
141
                cropRect = frameCropRect;
142
            } else {
143
                cropRect.add(frameCropRect);
144
            }
145
        }
146
147
        // Make crop rect one pixel larger (since we want an inclusive rect)
148
        if (cropRect != null) {
149
            cropRect.add(
150
                    cropRect.x + cropRect.width + 1,
151
                    cropRect.y + cropRect.height + 1);
152
        }
153
154
        return cropRect;
155
    }
156
157
    private static Rectangle determineCropRect(BufferedImage image)
158
    {
159
        // Loop through all the pixels, ignoring transparent ones.
160
        Rectangle rect = null;
161
162
        for (int y = 0; y < image.getHeight(); y++) {
163
            for (int x = 0; x < image.getWidth(); x++) {
164
                int color = image.getRGB(x, y);
165
166
                if (color != TRANSPARENT && (color & 0xFF000000) != 0) {
167
                    if (rect == null) {
168
                        rect = new Rectangle(x, y, 0, 0);
169
                    } else {
170
                        rect.add(x, y);
171
                    }
172
                }
173
            }
174
        }
175
176
        return rect;
177
    }
178
179
    private static void filterHairstyle(Vector<BufferedImage> spriteSet)
180
    {
181
        // Remove frame 1, 2, 6 and 7 from each color
182
        for (int i = HAIR_COLORS - 1; i >= 0; i--) {
183
            spriteSet.remove(i * HAIR_FRAMES + 7);
184
            spriteSet.remove(i * HAIR_FRAMES + 6);
185
            spriteSet.remove(i * HAIR_FRAMES + 2);
186
            spriteSet.remove(i * HAIR_FRAMES + 1);
187
        }
188
    }
189
190
    private static void filterHeadgear(Vector<BufferedImage> spriteSet)
191
    {
192
        // Remove frame 1, 2, 6 and 7
193
        spriteSet.remove(7);
194
        spriteSet.remove(6);
195
        spriteSet.remove(2);
196
        spriteSet.remove(1);
197
    }
198
}