// PpmDecoder - read in a PPM image // // Copyright (C) 1996 by Jef Poskanzer . All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions // are met: // 1. Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // 2. Redistributions in binary form must reproduce the above copyright // notice, this list of conditions and the following disclaimer in the // documentation and/or other materials provided with the distribution. // // THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE // ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS // OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY // OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF // SUCH DAMAGE. // // Visit the ACME Labs Java page for up-to-date versions of this and other // fine Java utilities: http://www.acme.com/java/ package Acme.JPM.Decoders; import java.io.*; import java.awt.image.*; /// Read in a PPM image. //

// Fetch the software.
// Fetch the entire Acme package. //

// @see Acme.JPM.Encoders.PpmEncoder public class PpmDecoder extends ImageDecoder { /// Constructor. // @param in The stream to read the bytes from. public PpmDecoder( InputStream in ) { super( in ); } private int type; private static final int PBM_ASCII = 1; private static final int PGM_ASCII = 2; private static final int PPM_ASCII = 3; private static final int PBM_RAW = 4; private static final int PGM_RAW = 5; private static final int PPM_RAW = 6; private int width = -1, height = -1; private int maxval; /// Subclasses implement this to read in enough of the image stream // to figure out the width and height. void readHeader( InputStream in ) throws IOException { char c1, c2; c1 = (char) readByte( in ); c2 = (char) readByte( in ); if ( c1 != 'P' ) throw new IOException( "not a PBM/PGM/PPM file" ); switch ( c2 ) { case '1': type = PBM_ASCII; break; case '2': type = PGM_ASCII; break; case '3': type = PPM_ASCII; break; case '4': type = PBM_RAW; break; case '5': type = PGM_RAW; break; case '6': type = PPM_RAW; break; default: throw new IOException( "not a standard PBM/PGM/PPM file" ); } width = readInt( in ); height = readInt( in ); if ( type != PBM_ASCII && type != PBM_RAW ) maxval = readInt( in ); } /// Subclasses implement this to return the width, or -1 if not known. int getWidth() { return width; } /// Subclasses implement this to return the height, or -1 if not known. int getHeight() { return height; } /// Subclasses implement this to read pixel data into the rgbRow // array, an int[width]. One int per pixel, no offsets or padding, // RGBdefault (AARRGGBB) color model void readRow( InputStream in, int row, int[] rgbRow ) throws IOException { int col, r, g, b; int rgb = 0; char c; for ( col = 0; col < width; ++col ) { switch ( type ) { case PBM_ASCII: c = readChar( in ); if ( c == '1' ) rgb = 0xff000000; else if ( c == '0' ) rgb = 0xffffffff; else throw new IOException( "illegal PBM bit" ); break; case PGM_ASCII: g = readInt( in ); rgb = makeRgb( g, g, g ); break; case PPM_ASCII: r = readInt( in ); g = readInt( in ); b = readInt( in ); rgb = makeRgb( r, g, b ); break; case PBM_RAW: if ( readBit( in ) ) rgb = 0xff000000; else rgb = 0xffffffff; break; case PGM_RAW: g = readByte( in ); if ( maxval != 255 ) g = fixDepth( g ); rgb = makeRgb( g, g, g ); break; case PPM_RAW: r = readByte( in ); g = readByte( in ); b = readByte( in ); if ( maxval != 255 ) { r = fixDepth( r ); g = fixDepth( g ); b = fixDepth( b ); } rgb = makeRgb( r, g, b ); break; } rgbRow[col] = rgb; } } /// Utility routine to read a byte. Instead of returning -1 on // EOF, it throws an exception. private static int readByte( InputStream in ) throws IOException { int b = in.read(); if ( b == -1 ) throw new EOFException(); return b; } private int bitshift = -1; private int bits; /// Utility routine to read a bit, packed eight to a byte, big-endian. private boolean readBit( InputStream in ) throws IOException { if ( bitshift == -1 ) { bits = readByte( in ); bitshift = 7; } boolean bit = ( ( ( bits >> bitshift ) & 1 ) != 0 ); --bitshift; return bit; } /// Utility routine to read a character, ignoring comments. private static char readChar( InputStream in ) throws IOException { char c; c = (char) readByte( in ); if ( c == '#' ) { do { c = (char) readByte( in ); } while ( c != '\n' && c != '\r' ); } return c; } /// Utility routine to read the first non-whitespace character. private static char readNonwhiteChar( InputStream in ) throws IOException { char c; do { c = readChar( in ); } while ( c == ' ' || c == '\t' || c == '\n' || c == '\r' ); return c; } /// Utility routine to read an ASCII integer, ignoring comments. private static int readInt( InputStream in ) throws IOException { char c; int i; c = readNonwhiteChar( in ); if ( c < '0' || c > '9' ) throw new IOException( "junk in file where integer should be" ); i = 0; do { i = i * 10 + c - '0'; c = readChar( in ); } while ( c >= '0' && c <= '9' ); return i; } /// Utility routine to rescale a pixel value from a non-eight-bit maxval. private int fixDepth( int p ) { return ( p * 255 + maxval / 2 ) / maxval; } /// Utility routine make an RGBdefault pixel from three color values. private static int makeRgb( int r, int g, int b ) { return 0xff000000 | ( r << 16 ) | ( g << 8 ) | b; } }