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