package uk.co.mmscomputing.imageio.ppm; import java.io.*; import java.util.*; import java.awt.image.*; import javax.imageio.*; import javax.imageio.spi.*; import javax.imageio.stream.*; import javax.imageio.metadata.*; public class PPMImageReader extends ImageReader { private boolean gotHeader = false; 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 format=-1; // 1 .. 6 private int width=-1; // Width private int height=-1; // Height private int maxcolval=0; // max colour value protected PPMImageReader(ImageReaderSpi originatingProvider){ super(originatingProvider); } public BufferedImage read(int imageIndex, ImageReadParam param)throws IOException{ checkIndex(imageIndex); return read((ImageInputStream)getInput()); } public int getHeight(int imageIndex)throws IOException{ checkIndex(imageIndex); readHeader((ImageInputStream)getInput()); return height; } public int getWidth(int imageIndex)throws IOException{ checkIndex(imageIndex); readHeader((ImageInputStream)getInput()); return width; } public Iterator getImageTypes(int imageIndex)throws IOException{ checkIndex(imageIndex); readHeader((ImageInputStream)getInput()); ImageTypeSpecifier imageType = null; java.util.List l = new ArrayList(); imageType=ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_INT_RGB); l.add(imageType); return l.iterator(); } public int getNumImages(boolean allowSearch)throws IOException{ return 1; } public IIOMetadata getImageMetadata(int imageIndex)throws IOException{ checkIndex(imageIndex); return null; } public IIOMetadata getStreamMetadata() throws IOException{ return null; } private void checkIndex(int imageIndex) { if (imageIndex != 0) { throw new IndexOutOfBoundsException(getClass().getName()+".checkIndex: Bad index in ppm image reader"); } } // ascii parser routines private char readAsciiChar(ImageInputStream in) throws IOException{ char c; do{ c=(char)in.read(); if(c=='#'){ // comment : read until end of line do{ c=(char)in.read(); }while((c!='\n')&&(c!='\r')); } }while((c==' ')||(c=='\t')||(c=='\n')||(c=='\r')); // white space return c; } private int readAsciiInt(ImageInputStream in)throws IOException{ char c=readAsciiChar(in); if((c<'0')||('9'<c)){ throw new IOException(getClass().getName()+".readAsciiInt: Expected ascii integer." ); } int i=0; do{ i=i*10+c-'0'; c=(char)in.read(); }while(('0'<=c)&&(c<='9')); return i; } private void readHeader(ImageInputStream in)throws IOException{ if (gotHeader) { return; } gotHeader = true; byte[] pn = new byte[2]; in.readFully(pn); if(pn[0]!=(byte)'P'){ throw new IOException(getClass().getName()+".readHeader: Invalid PPM File. Missing 'P'.");} format=pn[1]-'0'; switch(format){ case PBM_ASCII: case PGM_ASCII: case PPM_ASCII: throw new IOException(getClass().getName()+".readHeader: Unsupported ASCII PPM File Format : P"+pn[1]); case PBM_RAW: width=readAsciiInt(in); height=readAsciiInt(in); maxcolval=1; break; case PGM_RAW: width=readAsciiInt(in); height=readAsciiInt(in); maxcolval=readAsciiInt(in); break; case PPM_RAW: width=readAsciiInt(in); height=readAsciiInt(in); maxcolval=readAsciiInt(in); break; default: throw new IOException("Invalid PPM File. Unknown Format ["+format+"]"); } } private BufferedImage read(ImageInputStream in)throws IOException{ readHeader(in); byte[] data; switch(format){ case PBM_RAW: data = new byte[(width*height)>>3]; in.readFully(data); return pbm(width,height,data); case PGM_RAW: data = new byte[width*height]; in.readFully(data); return pgm(width,height,maxcolval,data); case PPM_RAW: data = new byte[width*height*3]; in.readFully(data); return ppm(width,height,maxcolval,data); default: throw new IOException(getClass().getName()+".read: Unsupported File Format."); } } static public BufferedImage ppm(int width, int height, int maxcolval, byte[] data){ if(maxcolval<256){ BufferedImage image=new BufferedImage(width,height,BufferedImage.TYPE_INT_RGB); int r,g,b,k=0,pixel; if(maxcolval==255){ // don't scale for(int y=0;y<height;y++){ for(int x=0;(x<width)&&((k+3)<data.length);x++){ r=data[k++] & 0xFF; g=data[k++] & 0xFF; b=data[k++] & 0xFF; pixel=0xFF000000+(r<<16)+(g<<8)+b; image.setRGB(x,y,pixel); } } }else{ for(int y=0;y<height;y++){ for(int x=0;(x<width)&&((k+3)<data.length);x++){ r=data[k++] & 0xFF;r=((r*255)+(maxcolval>>1))/maxcolval; // scale to 0..255 range g=data[k++] & 0xFF;g=((g*255)+(maxcolval>>1))/maxcolval; b=data[k++] & 0xFF;b=((b*255)+(maxcolval>>1))/maxcolval; pixel=0xFF000000+(r<<16)+(g<<8)+b; image.setRGB(x,y,pixel); } } } return image; }else{ // no 48 bit colour type available in java ? BufferedImage image=new BufferedImage(width,height,BufferedImage.TYPE_INT_RGB); int r,g,b,k=0,pixel; for(int y=0;y<height;y++){ for(int x=0;(x<width)&&((k+6)<data.length);x++){ r=(data[k++] & 0xFF)|((data[k++] & 0xFF)<<8);r=((r*255)+(maxcolval>>1))/maxcolval; // scale to 0..255 range g=(data[k++] & 0xFF)|((data[k++] & 0xFF)<<8);g=((g*255)+(maxcolval>>1))/maxcolval; b=(data[k++] & 0xFF)|((data[k++] & 0xFF)<<8);b=((b*255)+(maxcolval>>1))/maxcolval; pixel=0xFF000000+(r<<16)+(g<<8)+b; image.setRGB(x,y,pixel); } } return image; } } static public BufferedImage pgm(int width, int height, int maxcolval, byte[] data){ if(maxcolval<256){ BufferedImage image=new BufferedImage(width,height,BufferedImage.TYPE_BYTE_GRAY); WritableRaster raster=image.getRaster(); int g,k=0,pixel; if(maxcolval==255){ // don't scale for(int y=0;y<height;y++){ for(int x=0;(x<width)&&(k<data.length);x++){ raster.setSample(x,y,0,data[k++] & 0xFF); } } }else{ for(int y=0;y<height;y++){ for(int x=0;(x<width)&&(k<data.length);x++){ pixel=(((data[k++] & 0xFF)*255)+(maxcolval>>1))/maxcolval; // scale to 0..255 range raster.setSample(x,y,0,pixel); } } } return image; }else{ // 16 bit gray scale image BufferedImage image=new BufferedImage(width,height,BufferedImage.TYPE_USHORT_GRAY); WritableRaster raster=image.getRaster(); int g,k=0,sample,pixel; if(maxcolval==65535){ // don't scale for(int y=0;y<height;y++){ for(int x=0;(x<width)&&(k<data.length-1);x++){ sample=(data[k++] & 0xFF)|((data[k++] & 0xFF)<<8); raster.setSample(x,y,0,sample); } } }else{ for(int y=0;y<height;y++){ for(int x=0;(x<width)&&(k<data.length-1);x++){ sample=(data[k++] & 0xFF)|((data[k++] & 0xFF)<<8); pixel=((sample*65535)+(maxcolval>>1))/maxcolval; // scale to 0..65535 range raster.setSample(x,y,0,pixel); } } } return image; } } static public BufferedImage pbm(int width, int height, byte[] data){ BufferedImage image=new BufferedImage(width,height,BufferedImage.TYPE_BYTE_BINARY); WritableRaster raster=image.getRaster(); int k=0; int bytesPerLine=((width%8)==0)?width>>3:(width+8)>>3; for(int y=0;y<height;y++){ for(int x=0;(x<bytesPerLine)&&(k<data.length);x++){ byte b=data[k++]; for(int bit=0;bit<8;bit++){ int xx=(x<<3)+(7-bit); if(xx<width){ // last byte in line may have padding bits int pixel=((b&(1<<bit))==0)?0xFFFFFFFF:0xFF000000; // inversion raster.setSample(xx,y,0,pixel); } // else ignore padding bits } } } return image; } }