diff --git a/src/java/org/lwjgl/util/XPMFile.java b/src/java/org/lwjgl/util/XPMFile.java index fb5b5a14..acb89508 100644 --- a/src/java/org/lwjgl/util/XPMFile.java +++ b/src/java/org/lwjgl/util/XPMFile.java @@ -1,12 +1,13 @@ package org.lwjgl.util; + import java.io.BufferedOutputStream; -import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; +import java.io.LineNumberReader; import java.util.HashMap; import java.util.StringTokenizer; @@ -15,169 +16,209 @@ import java.util.StringTokenizer; *

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

+ * * @author Brian Matzon + * @author Jos Hirth * @version $Revision$ */ public class XPMFile { /** Array of bytes (RGBA) */ - private byte bytes[] = null; + private byte bytes[] = null; - /** Height of image */ - private int height = 0; + private final static int WIDTH = 0; - /** Width of image */ - private int width = 0; + private final static int HEIGHT = 1; - /* - * Private constructor, use load(String filename) + private final static int NUMBER_OF_COLORS = 2; + + private final static int CHARACTERS_PER_PIXEL = 3; + + private static int[] format = new int[4]; + + /* + * Private constructor, use load(String filename) */ - private XPMFile() { } - + private XPMFile() { + } + /** - * Loads the XPM file + * Loads the XPM file * - * @param file path to file - * @return XPMFile loaded, or exception - * @throws IOException If any IO exceptions occurs while reading file + * @param file + * path to file + * @return XPMFile loaded, or exception + * @throws IOException + * If any IO exceptions occurs while reading file */ public static XPMFile load(String file) throws IOException { return load(new FileInputStream(new File(file))); } - + /** - * Loads the XPM file + * Loads the XPM file * - * @param is InputStream to read file from - * @return XPMFile loaded, or exception - * @throws IOException If any IO exceptions occurs while reading file + * @param is + * InputStream to read file from + * @return XPMFile loaded, or exception + * @throws IOException + * If any IO exceptions occurs while reading file */ public static XPMFile load(InputStream is) throws IOException { XPMFile xFile = new XPMFile(); xFile.readImage(is); return xFile; - } + } - /** - * @return the height of the image. + /** + * @return the height of the image. */ public int getHeight() { - return height; + return format[HEIGHT]; } - /** - * @return the width of the image. + /** + * @return the width of the image. */ public int getWidth() { - return width; + return format[WIDTH]; } - /** - * @return The data of the image. + /** + * @return The data of the image. */ public byte[] getBytes() { return bytes; } - /** - * Read the image from the specified file. + /** + * Read the image from the specified file. * - * @throws IOException If any IO exceptions occurs while reading file + * @throws IOException + * If any IO exceptions occurs while reading file */ private void readImage(InputStream is) throws IOException { try { - BufferedReader br = new BufferedReader(new InputStreamReader(is)); + LineNumberReader reader = new LineNumberReader( + new InputStreamReader(is)); HashMap colors = new HashMap(); - - String comment = br.readLine(); - String typedef = br.readLine(); - int[] format = parseFormat(br.readLine()); - + + format = parseFormat(nextLineOfInterest(reader)); + // setup color mapping - for (int i = 0; i < format[2]; i++) { - Object[] colorDefinition = parseColor(br.readLine()); + for (int i = 0; i < format[NUMBER_OF_COLORS]; i++) { + Object[] colorDefinition = parseColor(nextLineOfInterest(reader)); 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); + bytes = new byte[format[WIDTH] * format[HEIGHT] * 4]; + for (int i = 0; i < format[HEIGHT]; i++) { + parseImageLine(nextLineOfInterest(reader), format, colors, i); } } catch (Exception e) { + e.printStackTrace(); throw new IllegalArgumentException("Unable to parse XPM File"); } } + /** + * Finds the next interesting line of text. + * + * @param reader + * The LineNumberReader to read from + * @return The next interesting String (with stripped quotes) + * @throws IOException + * If any IO exceptions occurs while reading file + */ + private String nextLineOfInterest(LineNumberReader reader) + throws IOException { + String ret; + do { + ret = reader.readLine(); + } while (!ret.startsWith("\"")); + // lacks sanity check + return ret.substring(1, ret.lastIndexOf('\"')); + } + /** * Parses the format of the xpm file given a format string - * - * @param format String to parse + * + * @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); + // 16 16 122 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 */ + 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 + * + * @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); + // line should look like this: + // # c #0A0A0A - String key = line.substring(0, 2); - String type = line.substring(3, 4); - String color = line.substring(6); + // NOTE: will break if the color is something like "black" or "gray50" + // etc (instead of #rrggbb). + + String key = line.substring(0, format[CHARACTERS_PER_PIXEL]); + // since we always assume color as type we dont need to read it + // String type = line.substring(format[CHARACTERS_PER_PIXEL] + 1, + // format[CHARACTERS_PER_PIXEL] + 2); + String color = line.substring(format[CHARACTERS_PER_PIXEL] + 4); // we always assume type is color, and supplied as # - return new Object[] { key, new Integer(Integer.parseInt(color, 16))}; + return new Object[] { key, new Integer(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 + * + * @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) { + private void parseImageLine(String line, int[] format, HashMap colors, + int index) { // offset for next line - int offset = index * 4 * format[0]; + int offset = index * 4 * format[WIDTH]; - // 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)); + // read characters times, + // each iteration equals one pixel + for (int i = 0; i < format[WIDTH]; i++) { + String key = line + .substring( + i * format[CHARACTERS_PER_PIXEL], + (i * format[CHARACTERS_PER_PIXEL] + format[CHARACTERS_PER_PIXEL])); 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 + 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 } } @@ -192,16 +233,16 @@ public class XPMFile { 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))); + BufferedOutputStream bos = new BufferedOutputStream( + new FileOutputStream(new File(out))); bos.write(file.getBytes()); bos.close(); - //showResult(file.getBytes()); + // 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); @@ -232,4 +273,4 @@ public class XPMFile { frame.setSize(100, 100); frame.setVisible(true); }*/ -} +} \ No newline at end of file