ページ更新: 2004-09-29 (水) (6043日前)
関連: 規格/画像/PPM 2004-09-29 作成中 PPM (Portable Pixel Map), PGM (Portable Gray Map) の読み書き。 [編集]規格 #
P3 - PPM, Text P6 - PPM, Binary P2 - PGM, Text P5 - PGM, Text
制限 #
ロジック #- ヘッダ処理 - 空白を読み飛ばす - # が見つかったら、改行コードまで読み飛ばす。 - 空白以外の文字が見つかったら、以下の処理を行う。 - S1: 'Pn' (nは数字) であれば、magicNumberとする。S2へ - S2: [0-9]+ を取り出して、widthとする。S3へ - S3: [0-9]+ を取り出して、heightとする。S4へ - S4: [0-9]+ を取り出して、maxとする。S5へ - S5: 空白以外であれば、データ開始。 - バイナリデータ処理 - width x heightの数だけデータを取り出す。[編集] 作りかけのコード (2005-09-11) #package jp.discypus.image; import java.io.BufferedInputStream; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.PushbackInputStream; import java.io.Reader; import java.io.StreamTokenizer; /** * Portable aNy Map Image Loader * */ public class PnmLoader { private static final String PBM_TEXT = "P1"; private static final String PGM_TEXT = "P2"; private static final String PPM_TEXT = "P3"; private static final String PBM_BINARY = "P4"; private static final String PGM_BINARY = "P5"; private static final String PPM_BINARY = "P6"; private static final int CR = 0x0d; private static final int LF = 0x0a; public static class Header { String magicNumber; int width; int height; int max; } public static class Image { int width; int height; int bytesPerPixel; byte[][] scanLines; } public Image loadImage(final InputStream in) throws IOException { final PushbackInputStream pis = new PushbackInputStream(in); // final Header header = loadHeader(pis); // if (PGM_BINARY.equals(header.magicNumber)) { final Image image = loadPgmBody(pis, header); return image; } else if (PPM_BINARY.equals(header.magicNumber)) { final Image image = loadPpmBody(pis, header); return image; } else if (PBM_BINARY.equals(header.magicNumber)) { throw new IllegalStateException("Unsupported image type:" + header.magicNumber); } else if (PBM_TEXT.equals(header.magicNumber)) { throw new IllegalStateException("Unsupported image type:" + header.magicNumber); } else if (PGM_TEXT.equals(header.magicNumber)) { throw new IllegalStateException("Unsupported image type:" + header.magicNumber); } else if (PPM_TEXT.equals(header.magicNumber)) { throw new IllegalStateException("Unsupported image type:" + header.magicNumber); } else { throw new IllegalStateException("Invalid image type='" + header.magicNumber + "'"); } } /** * @throws IOException */ private Header loadHeader(final PushbackInputStream in) throws IOException { final Header header = new Header(); skipWhiteSpecesAndComments(in); byte[] magicNumber = new byte[2]; in.read(magicNumber); header.magicNumber = new String(magicNumber); skipWhiteSpecesAndComments(in); header.width = readNumber(in); skipWhiteSpecesAndComments(in); header.height = readNumber(in); skipWhiteSpecesAndComments(in); header.max = readNumber(in); skipWhiteSpecesAndComments(in); return header; } /** * ホワイトスペース (空白、タブ、改行)とコメントを読み飛ばす。 * * @param in * @param header * @throws IOException */ private void skipWhiteSpecesAndComments(final PushbackInputStream in) throws IOException { /* * コメントは#から行末まで。 */ for (;;) { int ch = in.read(); if (ch == -1) { throw new IllegalStateException("stream is terminated"); } if (ch == '#') { // 改行まで読み飛ばす for (;;) { ch = in.read(); if ((ch == CR) || (ch == LF)) { break; } } // 改行がなくなるまで読み飛ばす for (;;) { ch = in.read(); if ((ch != CR) && (ch != LF)) { in.unread(ch); break; } } } else if (isWhitespece(ch)) { // 空白を読み飛ばす for (;;) { ch = in.read(); if (!isWhitespece(ch)) { in.unread(ch); break; } } } else { in.unread(ch); break; } } } private boolean isWhitespece(final int ch) { return (ch == ' ') || (ch == '\t') || (ch == CR) || (ch == LF); } private int readNumber(final PushbackInputStream in) throws IOException { final StringBuffer buf = new StringBuffer(); for (;;) { int ch = in.read(); if ((ch < '0') || ('9' < ch)) { in.unread(ch); break; } buf.append(ch - '0'); } int result = Integer.parseInt(buf.toString()); return result; } private Image loadPgmBody(final InputStream in, final Header header) throws IOException { final Image image = new Image(); image.width = header.width; image.height = header.height; image.bytesPerPixel = 1; image.scanLines = new byte[image.height][]; // for (int y = 0; y < image.height; y++) { final byte[] lineBuf = new byte[image.width * image.bytesPerPixel]; final int len = in.read(lineBuf); image.scanLines[y] = lineBuf; if (len < lineBuf.length) { break; } } return image; } private Image loadPpmBody(final InputStream in, final Header header) throws IOException { final Image image = new Image(); image.width = header.width; image.height = header.height; image.bytesPerPixel = 3; image.scanLines = new byte[image.height][]; // for (int y = 0; y < image.height; y++) { final byte[] lineBuf = new byte[image.width * image.bytesPerPixel]; final int len = in.read(lineBuf); image.scanLines[y] = lineBuf; if (len < lineBuf.length) { break; } } return image; } /** * Headerを読み込む。 * * TODO StreamTokenizerを使って作成中。 * @throws IOException */ private Header readHeader2(final InputStream in) throws IOException { final Reader reader = new InputStreamReader(in); final StreamTokenizer st = new StreamTokenizer(reader); st.commentChar('#'); st.wordChars('0', '9'); st.wordChars('a', 'z'); st.wordChars('A', 'Z'); st.whitespaceChars(' ', ' '); st.whitespaceChars('\n', '\n'); st.whitespaceChars('\r', '\r'); st.whitespaceChars('\t', '\t'); st.eolIsSignificant(false); // final int MODE_MAGIC_NUMBER_P = 0; final int MODE_MAGIC_NUMBER_N = 1; final int MODE_WIDTH = 2; final int MODE_HEIGHT = 3; final int MODE_MAX = 4; final int MODE_BODY = 5; final Header header = new Header(); int mode = MODE_MAGIC_NUMBER_P; loop: for (;;) { final int type = st.nextToken(); if (type == StreamTokenizer.TT_EOF) { break; } switch (type) { default: break loop; case StreamTokenizer.TT_EOL: break; case StreamTokenizer.TT_WORD: // Magic Numberの1桁目 switch (mode) { case MODE_MAGIC_NUMBER_P: if ("P".equalsIgnoreCase(st.sval)) { mode = MODE_MAGIC_NUMBER_N; } else { // エラー } break; default: // エラー break; } break; case StreamTokenizer.TT_NUMBER: // width, height, max, Magic Numberの2桁目 switch (mode) { case MODE_MAGIC_NUMBER_N: header.magicNumber = "P" + (int)st.nval; mode = MODE_WIDTH; break; case MODE_WIDTH: header.width = (int)st.nval; mode = MODE_HEIGHT; break; case MODE_HEIGHT: header.width = (int)st.nval; mode = MODE_MAX; break; case MODE_MAX: header.width = (int)st.nval; mode = MODE_BODY; break loop; default: // エラー break; } break; } } if (mode != MODE_BODY) { throw new IllegalStateException(""); } return header; } /** * 簡易テスト */ public static void main(final String[] args) { final String filename = "sample.ppm"; try { final BufferedInputStream bis = new BufferedInputStream(new FileInputStream(filename)); try { final Image image = new PnmLoader().loadImage(bis); } finally { if (bis != null) { bis.close(); } } } catch (final IOException e) { e.printStackTrace(); } } } |