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