diff --git a/src/java/org/lwjgl/util/XPMFile.java b/src/java/org/lwjgl/util/XPMFile.java new file mode 100644 index 00000000..d1065b1f --- /dev/null +++ b/src/java/org/lwjgl/util/XPMFile.java @@ -0,0 +1,226 @@ +package org.lwjgl.util; +import java.io.BufferedOutputStream; +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.util.HashMap; +import java.util.StringTokenizer; + +/** + * $Id$ + *

+ * NOTE: This simple XPM reader does not support extensions nor hotspots + *

+ * @author Brian Matzon + * @version $Revision$ + */ + +public class XPMFile { + + /** Array of bytes (RGBA) */ + private byte bytes[] = null; + + /** Height of image */ + private int height = 0; + + /** Width of image */ + private int width = 0; + + /* + * Private constructor, use load(String filename) + */ + private XPMFile() { } + + /** + * Loads the XPM file + * + * @param file path to file + * @return XPMFile loaded, or exception + * @throws FileNotFoundException If file isn't found + * @throws IOException If any IO exceptions occurs while reading file + */ + public static XPMFile load(String file) throws FileNotFoundException, IOException { + XPMFile xFile = new XPMFile(); + xFile.readImage(file); + return xFile; + } + + /** + * @return the height of the image. + */ + public int getHeight() { + return height; + } + + /** + * @return the width of the image. + */ + public int getWidth() { + return width; + } + + /** + * @return The data of the image. + */ + public byte[] getBytes() { + return bytes; + } + + /** + * Read the image from the specified file. + * + * @throws FileNotFoundException If file isn't found + * @throws IOException If any IO exceptions occurs while reading file + */ + private void readImage(String filename) throws FileNotFoundException, IOException { + try { + BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(new File(filename)))); + HashMap colors = new HashMap(); + + String comment = br.readLine(); + String typedef = br.readLine(); + int[] format = parseFormat(br.readLine()); + + // setup color mapping + for (int i = 0; i < format[2]; i++) { + Object[] colorDefinition = parseColor(br.readLine()); + colors.put(colorDefinition[0], colorDefinition[1]); + } + + // read actual image (convert to RGBA) + bytes = new byte[format[0] * format[1] * 4]; + for (int i = 0; i < format[1]; i++) { + parseImageLine(br.readLine(), format, colors, i); + } + } catch (Exception e) { + throw new IllegalArgumentException("Unable to parse XPM File"); + } + } + + /** + * Parses the format of the xpm file given a format string + * + * @param format String to parse + * @return Array specifying width, height, colors, characters per pixel + */ + private int[] parseFormat(String format) { + + // format should look like this: + // "16 16 122 2", + + // nuke first and last " and last , + format = format.substring(1, format.length() - 2); + + // tokenize it + StringTokenizer st = new StringTokenizer(format); + + return new int[] { + Integer.parseInt(st.nextToken()), /* width */ + Integer.parseInt(st.nextToken()), /* height */ + Integer.parseInt(st.nextToken()), /* colors */ + Integer.parseInt(st.nextToken()) /* chars per pixel */ + }; + } + + /** + * Given a line defining a color/pixel, parses this into an array containing + * a key and a color + * @param line Line to parse + * @return Array containing a key (String) and a color (Integer) + */ + private Object[] parseColor(String line) { + // line should look like this + // "# c #0A0A0A", + + // nuke first and last " + line = line.substring(1, line.length() - 2); + + String key = line.substring(0, 2); + String type = line.substring(3, 4); + String color = line.substring(6); + + // we always assume type is color, and supplied as # + return new Object[] { key, Integer.parseInt(color, 16)}; + } + + /** + * Parses an Image line into its byte values + * @param line Line of chars to parse + * @param format Format to expext it in + * @param colors Colors to lookup + * @param index current index into lines, we've reached + */ + private void parseImageLine(String line, int[] format, HashMap colors, int index) { + // offset for next line + int offset = index * 4 * format[0]; + + // nuke first " + line = line.substring(1, line.length()); + + // read format[3] characters format[0] times, each iteration is one color + for (int i = 0; i < format[0]; i++) { + String key = line.substring(i * 2, (i * 2 + 2)); + Integer color = (Integer) colors.get(key); + bytes[offset + (i * 4) ] = (byte) ((color.intValue() & 0x00ff0000) >> 16); + bytes[offset + ((i * 4) + 1)] = (byte) ((color.intValue() & 0x0000ff00) >> 8); + bytes[offset + ((i * 4) + 2)] = (byte) ((color.intValue() & 0x000000ff) >> 0); // looks better :) + bytes[offset + ((i * 4) + 3)] = (byte) 0xff; // always 0xff alpha + } + } + + /** + * @param args + */ + public static void main(String[] args) { + if (args.length != 1) { + System.out.println("usage:\nXPMFile "); + } + + try { + String out = args[0].substring(0, args[0].indexOf(".")) + ".raw"; + XPMFile file = XPMFile.load(args[0]); + BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(new File(out))); + bos.write(file.getBytes()); + bos.close(); + + //showResult(file.getBytes()); + } catch (Exception e) { + e.printStackTrace(); + } + } + + /* + private static void showResult(byte[] bytes) { + final BufferedImage i = new BufferedImage(16, 16, BufferedImage.TYPE_INT_ARGB); + int c = 0; + for (int y = 0; y < 16; y++) { + for (int x = 0; x < 16; x++) { + i.setRGB(x, y, (bytes[c] << 16) + (bytes[c + 1] << 8) + (bytes[c + 2] << 0) + (bytes[c + 3] << 24));//+(128<<24));// + c += 4; + } + } + + final Frame frame = new Frame("XPM Result"); + frame.add(new Canvas() { + + public void paint(Graphics g) { + g.drawImage(i, 0, 0, frame); + } + }); + + frame.addWindowListener(new WindowAdapter() { + + public void windowClosing(WindowEvent e) { + frame.dispose(); + } + + }); + + frame.setSize(100, 100); + frame.setVisible(true); + }*/ +}