001/*
002 * $RCSfile: PNMImageReader.java,v $
003 *
004 * 
005 * Copyright (c) 2005 Sun Microsystems, Inc. All  Rights Reserved.
006 * 
007 * Redistribution and use in source and binary forms, with or without
008 * modification, are permitted provided that the following conditions
009 * are met: 
010 * 
011 * - Redistribution of source code must retain the above copyright 
012 *   notice, this  list of conditions and the following disclaimer.
013 * 
014 * - Redistribution in binary form must reproduce the above copyright
015 *   notice, this list of conditions and the following disclaimer in 
016 *   the documentation and/or other materials provided with the
017 *   distribution.
018 * 
019 * Neither the name of Sun Microsystems, Inc. or the names of 
020 * contributors may be used to endorse or promote products derived 
021 * from this software without specific prior written permission.
022 * 
023 * This software is provided "AS IS," without a warranty of any 
024 * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND 
025 * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, 
026 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
027 * EXCLUDED. SUN MIDROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL 
028 * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF 
029 * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
030 * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR 
031 * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
032 * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
033 * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
034 * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
035 * POSSIBILITY OF SUCH DAMAGES. 
036 * 
037 * You acknowledge that this software is not designed or intended for 
038 * use in the design, construction, operation or maintenance of any 
039 * nuclear facility. 
040 *
041 * $Revision: 1.1 $
042 * $Date: 2005/02/11 05:01:40 $
043 * $State: Exp $
044 */
045package com.github.jaiimageio.impl.plugins.pnm;
046
047import java.awt.Point;
048import java.awt.Rectangle;
049import java.awt.Transparency;
050import java.awt.color.ColorSpace;
051import java.awt.image.BufferedImage;
052import java.awt.image.ColorModel;
053import java.awt.image.ComponentColorModel;
054import java.awt.image.ComponentSampleModel;
055import java.awt.image.DataBuffer;
056import java.awt.image.DataBufferByte;
057import java.awt.image.DataBufferInt;
058import java.awt.image.DataBufferUShort;
059import java.awt.image.IndexColorModel;
060import java.awt.image.MultiPixelPackedSampleModel;
061import java.awt.image.PixelInterleavedSampleModel;
062import java.awt.image.Raster;
063import java.awt.image.SampleModel;
064import java.awt.image.WritableRaster;
065
066import javax.imageio.IIOException;
067import javax.imageio.ImageReader;
068import javax.imageio.ImageReadParam;
069import javax.imageio.ImageTypeSpecifier;
070import javax.imageio.metadata.IIOMetadata;
071import javax.imageio.spi.ImageReaderSpi;
072import javax.imageio.stream.ImageInputStream;
073
074import java.io.*;
075import java.util.ArrayList;
076import java.util.Iterator;
077import java.util.StringTokenizer;
078
079import com.github.jaiimageio.impl.common.ImageUtil;
080
081/** This class is the Java Image IO plugin reader for PNM images.
082 *  It may subsample the image, clip the image, select sub-bands,
083 *  and shift the decoded image origin if the proper decoding parameter
084 *  are set in the provided <code>PNMImageReadParam</code>.
085 */
086public class PNMImageReader extends ImageReader {
087    private static final int PBM_ASCII  = '1';
088    private static final int PGM_ASCII  = '2';
089    private static final int PPM_ASCII  = '3';
090    private static final int PBM_RAW    = '4';
091    private static final int PGM_RAW    = '5';
092    private static final int PPM_RAW    = '6';
093
094    private static final int LINE_FEED = 0x0A;
095    private static byte[] lineSeparator;
096
097    static {
098        if (lineSeparator == null) {
099            String ls = (String)java.security.AccessController.doPrivileged(
100               new sun.security.action.GetPropertyAction("line.separator"));
101            lineSeparator = ls.getBytes();
102        }
103    }
104
105    /** File variant: PBM/PGM/PPM, ASCII/RAW. */
106    private int variant;
107
108    /** Maximum pixel value. */
109    private int maxValue;
110
111    /** The input stream where reads from */
112    private ImageInputStream iis = null;
113
114    /** Indicates whether the header is read. */
115    private boolean gotHeader = false;
116
117    /** The stream position where the image data starts. */
118    private long imageDataOffset;
119
120    /** The original image width. */
121    private int width;
122
123    /** The original image height. */
124    private int height;
125
126    private String aLine;
127    private StringTokenizer token;
128
129    private PNMMetadata metadata;
130
131    /** Constructs <code>PNMImageReader</code> from the provided
132     *  <code>ImageReaderSpi</code>.
133     */
134    public PNMImageReader(ImageReaderSpi originator) {
135        super(originator);
136    }
137
138    /** Overrides the method defined in the superclass. */
139    public void setInput(Object input,
140                         boolean seekForwardOnly,
141                         boolean ignoreMetadata) {
142        super.setInput(input, seekForwardOnly, ignoreMetadata);
143        iis = (ImageInputStream) input; // Always works
144    }
145
146    /** Overrides the method defined in the superclass. */
147    public int getNumImages(boolean allowSearch) throws IOException {
148        return 1;
149    }
150
151    public int getWidth(int imageIndex) throws IOException {
152        checkIndex(imageIndex);
153        readHeader();
154        return width;
155    }
156
157    public int getHeight(int imageIndex) throws IOException {
158        checkIndex(imageIndex);
159        readHeader();
160        return height;
161    }
162
163    public int getVariant() {
164        return variant;
165    }
166
167    public int getMaxValue() {
168        return maxValue;
169    }
170
171    private void checkIndex(int imageIndex) {
172        if (imageIndex != 0) {
173            throw new IndexOutOfBoundsException(I18N.getString("PNMImageReader1"));
174        }
175    }
176
177    public synchronized void readHeader() throws IOException {
178        if (gotHeader) {
179            // Seek to where the image data starts, since that is where
180            // the stream pointer should be after header is read
181            iis.seek(imageDataOffset);
182            return;
183        }
184
185        if (iis != null) {
186            if (iis.readByte() != 'P') {        // magic number
187                throw new RuntimeException(I18N.getString("PNMImageReader0"));
188            }
189
190            variant = iis.readByte();   // file variant
191            if ((variant < PBM_ASCII) || (variant > PPM_RAW)) {
192                throw new RuntimeException(I18N.getString("PNMImageReader0"));
193            }
194
195            // Create the metadata object.
196            metadata = new PNMMetadata();
197
198            // Set the variant.
199            metadata.setVariant(variant);
200
201            // Read the line separator.
202            iis.readLine();
203
204            readComments(iis, metadata);
205
206            width = readInteger(iis);   // width
207            height = readInteger(iis);  // height
208
209            if (variant == PBM_ASCII || variant == PBM_RAW) {
210                maxValue = 1;
211            } else {
212                maxValue = readInteger(iis);    // maximum value
213            }
214
215            metadata.setWidth(width);
216            metadata.setHeight(height);
217            metadata.setMaxBitDepth(maxValue);
218
219            gotHeader = true;
220
221            // Store the stream position where the image data starts
222            imageDataOffset = iis.getStreamPosition();
223        }
224    }
225
226    public Iterator getImageTypes(int imageIndex)
227        throws IOException {
228        checkIndex(imageIndex);
229
230        readHeader();
231        int tmp = (variant - '1') % 3 ;
232
233        ArrayList list = new ArrayList(1);
234        int dataType = DataBuffer.TYPE_INT;
235        // Determine data type based on maxValue.
236        if (maxValue < 0x100) {
237            dataType = DataBuffer.TYPE_BYTE;
238        } else if (maxValue < 0x10000) {
239            dataType = DataBuffer.TYPE_USHORT;
240        }
241
242        // Choose an appropriate SampleModel.
243        SampleModel sampleModel = null;
244        ColorModel colorModel = null;
245        if ((variant == PBM_ASCII) || (variant == PBM_RAW)) {
246            // Each pixel takes 1 bit, pack 8 pixels into a byte.
247            sampleModel = new MultiPixelPackedSampleModel(
248                              DataBuffer.TYPE_BYTE,
249                              width,
250                              height,
251                              1);
252            byte[] color = {(byte)0xFF, (byte)0};
253            colorModel = new IndexColorModel(1, 2, color, color, color);
254        } else {
255            sampleModel =
256                new PixelInterleavedSampleModel(dataType,
257                                                width,
258                                                height,
259                                                tmp == 1 ? 1 : 3,
260                                                width * (tmp == 1 ? 1 : 3),
261                                                tmp == 1 ? new int[]{0} : new int[]{0, 1, 2});
262
263            colorModel = ImageUtil.createColorModel(null, sampleModel);
264        }
265
266        list.add(new ImageTypeSpecifier(colorModel, sampleModel));
267
268        return list.iterator();
269    }
270
271    public ImageReadParam getDefaultReadParam() {
272        return new ImageReadParam();
273    }
274
275    public IIOMetadata getImageMetadata(int imageIndex)
276        throws IOException {
277        checkIndex(imageIndex);
278        readHeader();
279        return metadata;
280    }
281
282    public IIOMetadata getStreamMetadata() throws IOException {
283        return null;
284    }
285
286    public boolean isRandomAccessEasy(int imageIndex) throws IOException {
287        checkIndex(imageIndex);
288        return true;
289    }
290
291    public BufferedImage read(int imageIndex, ImageReadParam param)
292        throws IOException {
293        checkIndex(imageIndex);
294        clearAbortRequest();
295        processImageStarted(imageIndex);
296
297        if (param == null)
298            param = getDefaultReadParam();
299
300        //read header
301        readHeader();
302
303        Rectangle sourceRegion = new Rectangle(0, 0, 0, 0);
304        Rectangle destinationRegion = new Rectangle(0, 0, 0, 0);
305
306        computeRegions(param, this.width, this.height,
307                       param.getDestination(),
308                       sourceRegion,
309                       destinationRegion);
310
311        int scaleX = param.getSourceXSubsampling();
312        int scaleY = param.getSourceYSubsampling();
313
314        // If the destination band is set used it
315        int[] sourceBands = param.getSourceBands();
316        int[] destBands = param.getDestinationBands();
317
318        boolean seleBand = (sourceBands != null) && (destBands != null);
319        boolean noTransform =
320            destinationRegion.equals(new Rectangle(0, 0, width, height)) ||
321                              seleBand;
322
323        // The RAWBITS format can only support byte image data, which means
324        // maxValue should be less than 0x100. In case there's a conflict,
325        // base the maxValue on variant.
326        if (isRaw(variant) && maxValue >= 0x100) {
327            maxValue = 0xFF;
328        }
329
330        int numBands = 1;
331        // Determine number of bands: pixmap (PPM) is 3 bands,
332        // bitmap (PBM) and greymap (PGM) are 1 band.
333        if (variant == PPM_ASCII || variant == PPM_RAW) {
334            numBands = 3;
335        }
336
337        if (!seleBand) {
338            sourceBands = new int[numBands];
339            destBands = new int[numBands];
340            for (int i = 0; i < numBands; i++)
341                destBands[i] = sourceBands[i] = i;
342        }
343
344        int dataType = DataBuffer.TYPE_INT;
345        // Determine data type based on maxValue.
346        if (maxValue < 0x100) {
347            dataType = DataBuffer.TYPE_BYTE;
348        } else if (maxValue < 0x10000) {
349            dataType = DataBuffer.TYPE_USHORT;
350        }
351
352        // Choose an appropriate SampleModel.
353        SampleModel sampleModel = null;
354        ColorModel colorModel = null;
355        if ((variant == PBM_ASCII) || (variant == PBM_RAW)) {
356            // Each pixel takes 1 bit, pack 8 pixels into a byte.
357            sampleModel = new MultiPixelPackedSampleModel(
358                              DataBuffer.TYPE_BYTE,
359                              destinationRegion.width,
360                              destinationRegion.height,
361                              1);
362            byte[] color = {(byte)0xFF, (byte)0};
363            colorModel = new IndexColorModel(1, 2, color, color, color);
364        } else {
365            sampleModel = 
366                new PixelInterleavedSampleModel(dataType,
367                                                destinationRegion.width,
368                                                destinationRegion.height,
369                                                sourceBands.length,
370                                                destinationRegion.width * 
371                                                sourceBands.length,
372                                                destBands);
373
374            colorModel = ImageUtil.createColorModel(null, sampleModel);
375        }
376
377        // If the destination is provided, then use it. Otherwise, create new
378        // one
379        BufferedImage bi = param.getDestination();
380
381        // Get the image data.
382        WritableRaster raster = null;
383
384        if (bi == null) {
385            sampleModel = sampleModel.createCompatibleSampleModel(
386                               destinationRegion.x + destinationRegion.width,
387                               destinationRegion.y + destinationRegion.height);
388            if (seleBand)
389                sampleModel = sampleModel.createSubsetSampleModel(sourceBands);
390
391            raster = Raster.createWritableRaster(sampleModel, new Point());
392            bi = new BufferedImage(colorModel, raster, false, null);
393        } else {
394            raster = bi.getWritableTile(0, 0);
395            sampleModel = bi.getSampleModel();
396            colorModel = bi.getColorModel();
397            noTransform &= destinationRegion.equals(raster.getBounds());
398        }
399
400        switch (variant) {
401            case PBM_RAW:
402            {
403
404                // SampleModel for these cases should be MultiPixelPacked.
405                DataBuffer dataBuffer = raster.getDataBuffer();
406
407                // Read the entire image.
408                byte[] buf = ((DataBufferByte)dataBuffer).getData();
409                if (noTransform) {
410                    iis.readFully(buf, 0, buf.length);
411                    processImageUpdate(bi,
412                                       0, 0,
413                                       width, height, 1, 1,
414                                       destBands);
415                    processImageProgress(100.0F);
416                } else if (scaleX == 1  && sourceRegion.x % 8 == 0) {
417                    int skip = sourceRegion.x >> 3;
418                    int originalLS = width + 7 >> 3;
419                    int destLS = raster.getWidth() + 7 >> 3;
420
421                    int readLength = sourceRegion.width + 7 >> 3;
422                    int offset = sourceRegion.y * originalLS;
423                    iis.skipBytes(offset + skip);
424                    offset = originalLS * (scaleY - 1) + originalLS - readLength;
425                    byte[] lineData = new byte[readLength];
426
427                    int bitoff = destinationRegion.x & 7;
428                    boolean reformat = !(bitoff == 0);
429
430                    for (int i = 0, j = 0,
431                        k = destinationRegion.y * destLS + (destinationRegion.x >> 3);
432                        i < destinationRegion.height; i++, j += scaleY) {
433                        if (reformat) {
434                            iis.read(lineData, 0, readLength);
435                            int mask1 = (255 << bitoff) & 255;
436                            int mask2 = ~mask1 & 255;
437                            int shift = 8 - bitoff;
438
439                            int n = 0;
440                            int m = k;
441                            for (; n < readLength -1; n++, m++)
442                                buf[m] = (byte)(((lineData[n] & mask2) << shift) |
443                                                (lineData[n + 1] & mask1) >>bitoff);
444                            buf[m] = (byte)((lineData[n] & mask2) << shift);
445                        } else {
446                            iis.read(buf, k, readLength);
447                        }
448
449                        iis.skipBytes(offset);
450                        k += destLS;
451
452                        processImageUpdate(bi,
453                                           0, i,
454                                           destinationRegion.width, 1, 1, 1,
455                                           destBands);
456                        processImageProgress(100.0F*i/destinationRegion.height);
457                    }
458                } else {
459                    int originalLS = width + 7 >> 3;
460                    byte[] data = new byte[originalLS];
461                    iis.skipBytes(sourceRegion.y * originalLS);
462                    int destLS = bi.getWidth() + 7 >> 3;
463                    int offset = originalLS * (scaleY - 1);
464                    int dsx = destLS * destinationRegion.y +
465                              (destinationRegion.x >> 3);
466                    for (int i = 0, j = 0, n = dsx;
467                        i < destinationRegion.height; i++, j += scaleY) {
468                        iis.read(data, 0, originalLS);
469                        iis.skipBytes(offset);
470
471                        int b = 0;
472                        int pos = 7 - (destinationRegion.x & 7);
473                        for (int m = sourceRegion.x;
474                            m < sourceRegion.x + sourceRegion.width;
475                            m += scaleX) {
476                            b |= (data[m >> 3] >> (7 - (m & 7)) & 1) << pos;
477                            pos--;
478                            if (pos == -1) {
479                                buf[n++] = (byte)b;
480                                b = 0;
481                                pos = 7;
482                            }
483                        }
484
485                        if (pos != 7)
486                            buf[n++] = (byte)b;
487
488                        n += destinationRegion.x >> 3;
489                        processImageUpdate(bi,
490                                           0, i,
491                                           destinationRegion.width, 1, 1, 1,
492                                           destBands);
493                        processImageProgress(100.0F*i/destinationRegion.height);
494                    }
495                }
496                break;
497            }
498            case PBM_ASCII:
499            {
500                DataBuffer dataBuffer = raster.getDataBuffer();
501                byte[] buf = ((DataBufferByte)dataBuffer).getData();
502                if (noTransform)
503                    for (int i = 0, n = 0; i < height; i++) {
504                        int b = 0;
505                        int pos = 7;
506                        for (int j = 0; j < width; j++) {
507                            b |= (readInteger(iis) & 1) << pos;
508                            pos--;
509                            if (pos == -1 ) {
510                                buf[n++] = (byte)b;
511                                b = 0;
512                                pos = 7;
513                            }
514                        }
515                        if (pos != 7)
516                            buf[n++] = (byte)b;
517                        processImageUpdate(bi,
518                                           0, i,
519                                           width, 1, 1, 1,
520                                           destBands);
521                        processImageProgress(100.0F * i / height);
522                    }
523                else {
524                    skipInteger(iis, sourceRegion.y * width + sourceRegion.x);
525                    int skipX = scaleX - 1;
526                    int skipY = (scaleY - 1) * width +
527                                width - destinationRegion.width * scaleX;
528                    int dsx = (bi.getWidth() + 7 >> 3) *
529                              destinationRegion.y + (destinationRegion.x >> 3);
530                    for (int i = 0, n = dsx; i < destinationRegion.height; i++) {
531                        int b = 0;
532                        int pos = 7 - (destinationRegion.x & 7);
533                        for (int j = 0; j < destinationRegion.width; j++) {
534                            b |= (readInteger(iis) & 1) << pos;
535                            pos--;
536                            if (pos == -1 ) {
537                                buf[n++] = (byte)b;
538                                b = 0;
539                                pos = 7;
540                            }
541                            skipInteger(iis, skipX);
542                        }
543                        if (pos != 7)
544                            buf[n++] = (byte)b;
545
546                        n += destinationRegion.x >> 3;
547                        skipInteger(iis, skipY);
548                        processImageUpdate(bi,
549                                           0, i,
550                                           destinationRegion.width, 1, 1, 1,
551                                           destBands);
552                        processImageProgress(100.0F*i/destinationRegion.height);
553                    }
554                }
555                break;
556            }
557            case PGM_ASCII:
558            case PGM_RAW:
559            case PPM_ASCII:
560            case PPM_RAW:
561                // SampleModel for these cases should be PixelInterleaved.
562                int skipX = (scaleX - 1) * numBands;
563                int skipY = (scaleY * width -
564                            destinationRegion.width * scaleX) * numBands;
565                int dsx = (bi.getWidth() * destinationRegion. y +
566                          destinationRegion.x) * numBands;
567                switch (dataType) {
568                case DataBuffer.TYPE_BYTE:
569                    DataBufferByte bbuf =
570                        (DataBufferByte)raster.getDataBuffer();
571                    byte[] byteArray = bbuf.getData();
572                    if (isRaw(variant)) {
573                        if (noTransform) {
574                            iis.readFully(byteArray);
575                            processImageUpdate(bi,
576                                           0, 0,
577                                           width, height, 1, 1,
578                                           destBands);
579                            processImageProgress(100.0F);
580                        } else {
581                            iis.skipBytes(sourceRegion.y * width * numBands);
582                            int skip = (scaleY - 1) * width * numBands;
583                            byte[] data = new byte[width * numBands];
584                            int pixelStride = scaleX * numBands;
585                            int sx = sourceRegion.x * numBands;
586                            int ex = width;
587                            for (int i = 0, n = dsx ; i < destinationRegion.height; i++) {
588                                iis.read(data);
589                                for (int j = sourceRegion.x, k = sx;
590                                    j < sourceRegion.x + sourceRegion.width;
591                                    j+= scaleX, k += pixelStride) {
592                                    for (int m = 0; m < sourceBands.length; m++)
593                                        byteArray[n+ destBands[m]] = data[k + sourceBands[m]];
594                                    n += sourceBands.length;
595                                }
596                                n += destinationRegion.x * numBands;
597                                iis.skipBytes(skip);
598                                processImageUpdate(bi,
599                                                   0, i,
600                                                   destinationRegion.width, 1, 1, 1,
601                                                   destBands);
602                                processImageProgress(100.0F*i/destinationRegion.height);
603                            }
604                        }
605                    } else {
606                        skipInteger(iis,
607                                    (sourceRegion.y * width + sourceRegion.x) *
608                                    numBands);
609
610                        if (seleBand) {
611                            byte[] data = new byte[numBands];
612                            for (int i = 0, n = dsx; i < destinationRegion.height; i++) {
613                                for (int j = 0; j < destinationRegion.width; j++) {
614                                    for (int k = 0; k < numBands; k++)
615                                        data[k] = (byte)readInteger(iis);
616                                    for (int k = 0; k < sourceBands.length; k++)
617                                        byteArray[n+destBands[k]] = data[sourceBands[k]];
618                                    n += sourceBands.length;
619                                    skipInteger(iis, skipX);
620                                }
621                                n += destinationRegion.x * sourceBands.length;
622                                skipInteger(iis, skipY);
623                                processImageUpdate(bi,
624                                                   0, i,
625                                                   destinationRegion.width, 1, 1, 1,
626                                                   destBands);
627                                processImageProgress(100.0F*i/destinationRegion.height);
628                            }
629                        } else
630                            for (int i = 0, n = dsx; i < destinationRegion.height; i++) {
631                                for (int j = 0; j < destinationRegion.width; j++) {
632                                    for (int k = 0; k < numBands; k++)
633                                        byteArray[n++] = (byte)readInteger(iis);
634                                    skipInteger(iis, skipX);
635                                }
636                                n += destinationRegion.x * sourceBands.length;
637                                skipInteger(iis, skipY);
638                                processImageUpdate(bi,
639                                                   0, i,
640                                                   destinationRegion.width, 1, 1, 1,
641                                                   destBands);
642                                processImageProgress(100.0F*i/destinationRegion.height);
643                            }
644                    }
645                    break;
646
647                case DataBuffer.TYPE_USHORT:
648                    DataBufferUShort sbuf =
649                        (DataBufferUShort)raster.getDataBuffer();
650                    short[] shortArray = sbuf.getData();
651                    skipInteger(iis, sourceRegion.y * width * numBands + sourceRegion.x);
652
653                    if (seleBand) {
654                        short[] data = new short[numBands];
655                        for (int i = 0, n = dsx; i < destinationRegion.height; i++) {
656                            for (int j = 0; j < destinationRegion.width; j++) {
657                                for (int k = 0; k < numBands; k++)
658                                    data[k] = (short)readInteger(iis);
659                                for (int k = 0; k < sourceBands.length; k++)
660                                    shortArray[n+destBands[k]] = data[sourceBands[k]];
661                                n += sourceBands.length;
662                                skipInteger(iis, skipX);
663                            }
664                            n += destinationRegion.x * sourceBands.length;
665                            skipInteger(iis, skipY);
666                            processImageUpdate(bi,
667                                               0, i,
668                                               destinationRegion.width, 1, 1, 1,
669                                               destBands);
670                            processImageProgress(100.0F*i/destinationRegion.height);
671                        }
672                    } else
673                        for (int i = 0, n = dsx; i < destinationRegion.height; i++) {
674                            for (int j = 0; j < destinationRegion.width; j++) {
675                                for (int k = 0; k < numBands; k++)
676                                    shortArray[n++] = (short)readInteger(iis);
677                                skipInteger(iis, skipX);
678                            }
679                            n += destinationRegion.x * sourceBands.length;
680                            skipInteger(iis, skipY);
681                            processImageUpdate(bi,
682                                               0, i,
683                                               destinationRegion.width, 1, 1, 1,
684                                               destBands);
685                            processImageProgress(100.0F*i/destinationRegion.height);
686                        }
687                    break;
688
689                case DataBuffer.TYPE_INT:
690                    DataBufferInt ibuf =
691                        (DataBufferInt)raster.getDataBuffer();
692                    int[] intArray = ibuf.getData();
693                    skipInteger(iis, sourceRegion.y * width * numBands + sourceRegion.x);
694                    if (seleBand) {
695                        int[] data = new int[numBands];
696                        for (int i = 0, n = dsx; i < destinationRegion.height; i++) {
697                            for (int j = 0; j < destinationRegion.width; j++) {
698                                for (int k = 0; k < numBands; k++)
699                                    data[k] = readInteger(iis);
700                                for (int k = 0; k < sourceBands.length; k++)
701                                    intArray[n+destBands[k]] = data[sourceBands[k]];
702                                n += sourceBands.length;
703                                skipInteger(iis, skipX);
704                            }
705                            n += destinationRegion.x * sourceBands.length;
706                            skipInteger(iis, skipY);
707                            processImageUpdate(bi,
708                                               0, i,
709                                               destinationRegion.width, 1, 1, 1,
710                                               destBands);
711                            processImageProgress(100.0F*i/destinationRegion.height);
712                        }
713                    } else
714                        for (int i = 0, n = dsx; i < destinationRegion.height; i++) {
715                            for (int j = 0; j < destinationRegion.width; j++) {
716                                for (int k = 0; k < numBands; k++)
717                                    intArray[n++] = readInteger(iis);
718                                skipInteger(iis, skipX);
719                            }
720                            n += destinationRegion.x * sourceBands.length;
721                            skipInteger(iis, skipY);
722                            processImageUpdate(bi,
723                                               0, i,
724                                               destinationRegion.width, 1, 1, 1,
725                                               destBands);
726                            processImageProgress(100.0F*i/destinationRegion.height);
727                        }
728                    break;
729                }
730                break;
731        }
732
733        if (abortRequested())
734            processReadAborted();
735        else
736            processImageComplete();
737        return bi;
738    }
739
740    public boolean canReadRaster() {
741        return true;
742    }
743
744    public Raster readRaster(int imageIndex,
745                             ImageReadParam param) throws IOException {
746        BufferedImage bi = read(imageIndex, param);
747        return bi.getData();
748    }
749
750    public void reset() {
751        super.reset();
752        iis = null;
753        gotHeader = false;
754        System.gc();
755    }
756
757    /** Returns true if file variant is raw format, false if ASCII. */
758    private boolean isRaw(int v) {
759        return (v >= PBM_RAW);
760    }
761
762    /** Reads the comments. */
763    private void readComments(ImageInputStream stream,
764                              PNMMetadata metadata) throws IOException {
765        String line = null;
766        int pos = -1;
767        stream.mark();
768        while ((line = stream.readLine()) != null &&
769               (pos = line.indexOf("#")) >= 0) {
770            metadata.addComment(line.substring(pos + 1).trim());
771        }
772        stream.reset();
773    }
774
775    /** Reads the next integer. */
776    private int readInteger(ImageInputStream stream) throws IOException {
777        boolean foundDigit = false;
778
779        while (aLine == null) {
780            aLine = stream.readLine();
781            if (aLine == null)
782                return 0;
783            int pos = aLine.indexOf("#");
784            if (pos == 0)
785                aLine = null;
786            else if (pos > 0)
787                aLine = aLine.substring(0, pos - 1);
788
789            if (aLine != null)
790                token = new StringTokenizer(aLine);
791        }
792
793        while (token.hasMoreTokens()) {
794            String s = token.nextToken();
795
796            try {
797                return new Integer(s).intValue();
798            } catch (NumberFormatException e) {
799                continue;
800            }
801        }
802
803        if (!foundDigit) {
804            aLine = null;
805            return readInteger(stream);
806        }
807
808        return 0;
809    }
810
811    private void skipInteger(ImageInputStream stream, int num) throws IOException {
812        for (int i = 0; i < num; i++)
813            readInteger(stream);
814    }
815}