001    /*
002     * IFFCodec
003     *
004     * Copyright (c) 2000, 2001, 2002, 2003 Marco Schmidt.
005     * All rights reserved.
006     */
007    
008    package net.sourceforge.jiu.codecs;
009    
010    import java.io.DataInput;
011    import java.io.IOException;
012    import net.sourceforge.jiu.codecs.ImageCodec;
013    import net.sourceforge.jiu.codecs.InvalidFileStructureException;
014    import net.sourceforge.jiu.codecs.UnsupportedTypeException;
015    import net.sourceforge.jiu.codecs.WrongFileFormatException;
016    import net.sourceforge.jiu.data.MemoryPaletted8Image;
017    import net.sourceforge.jiu.data.MemoryRGB24Image;
018    import net.sourceforge.jiu.data.PixelImage;
019    import net.sourceforge.jiu.data.Palette;
020    import net.sourceforge.jiu.data.Paletted8Image;
021    import net.sourceforge.jiu.data.RGB24Image;
022    import net.sourceforge.jiu.ops.MissingParameterException;
023    import net.sourceforge.jiu.ops.OperationFailedException;
024    import net.sourceforge.jiu.ops.WrongParameterException;
025    
026    /**
027     * A codec to read Amiga IFF image files.
028     * IFF (Interchange File Format) is an Amiga wrapper file format for texts, images, animations, sound and other kinds of data.
029     * This codec only deals with image IFF files.
030     * Typical file extensions for IFF image files are <code>.lbm</code> and <code>.iff</code>.
031     * <h3>Loading / saving</h3>
032     * Only loading is supported by this codec.
033     * <h3>Supported file types</h3>
034     * Both uncompressed and run-length encoded files are read.
035     * <ul>
036     * <li>1 to 8 bit indexed (paletted) color</li>
037     * <li>24 bit RGB truecolor</li>
038     * <li>HAM6 and HAM8 images (which are a mixture of paletted and truecolor)</li>
039     * </ul>
040     * <h3>Usage example</h3>
041     * <pre>
042     * IFFCodec codec = new IFFCodec();
043     * codec.setFile("image.iff", CodecMode.LOAD);
044     * codec.process();
045     * PixelImage image = codec.getImage();
046     * </pre>
047     * @author Marco Schmidt
048     * @since 0.3.0
049     */
050    public class IFFCodec extends ImageCodec
051    {
052            private final static int MAGIC_BMHD = 0x424d4844;
053            private final static int MAGIC_BODY = 0x424f4459;
054            private final static int MAGIC_CMAP = 0x434d4150;
055            private final static int MAGIC_CAMG = 0x43414d47;
056            private final static int MAGIC_FORM = 0x464f524d;
057            private final static int MAGIC_ILBM = 0x494c424d;
058            private final static int MAGIC_PBM = 0x50424d20;
059            private final static int SIZE_BMHD = 0x00000014;
060            private final static byte COMPRESSION_NONE = 0x00;
061            private final static byte COMPRESSION_RLE = 0x01;
062            private final static byte COMPRESSION_UNKNOWN = 0x7f;
063            private int camg;
064            private byte compression;
065            private boolean ehb;
066            private boolean ham;
067            private boolean ham6;
068            private boolean ham8;
069            private int height;
070            private int masking;
071            private int numPlanes;
072            private Palette palette;
073            private boolean rgb24;
074            private int type;
075            private int width;
076    
077            /**
078             * Initializes all internal fields of this class.
079             */
080            /*public IFFCodec()
081            {
082                    super();
083                    compression = COMPRESSION_UNKNOWN;
084                    ehb = false;
085                    ham = false;
086                    ham6 = false;
087                    ham8 = false;
088                    height = -1;
089                    masking = -1;
090                    numPlanes = -1;
091                    rgb24 = false;
092                    palette = null;
093                    type = -1;
094                    width = -1;
095            }*/
096    
097            private void checkAndLoad() throws 
098                    InvalidFileStructureException, 
099                    IOException, 
100                    MissingParameterException, 
101                    UnsupportedTypeException, 
102                    WrongFileFormatException,
103                    WrongParameterException
104            {
105                    DataInput in = getInputAsDataInput();
106                    if (in == null)
107                    {
108                            throw new MissingParameterException("InputStream / DataInput object is missing.");
109                    }
110                    int formMagic = in.readInt();
111                    if (formMagic != MAGIC_FORM)
112                    {
113                            throw new WrongFileFormatException("Cannot load image. The " +
114                                    "input stream is not a valid IFF file (wrong magic byte " +
115                                    "sequence).");
116                    }
117                    int fileSize = in.readInt();
118                    type = in.readInt();
119                    if (type != MAGIC_ILBM && type != MAGIC_PBM)
120                    {
121                            throw new UnsupportedTypeException("Cannot load image. The " +
122                                    "input stream is an IFF file, but not of type ILBM or PBM" + 
123                                    " (" + getChunkName(type) + ")");
124                    }
125                    PixelImage result = null;
126                    boolean hasBMHD = false;
127                    boolean hasCAMG = false;
128                    boolean hasCMAP = false;
129                    do
130                    {
131                            int magic = in.readInt();
132                            //System.out.println(chunkNameToString(magic));
133                            int size = in.readInt();
134                            // chunks must always have an even number of bytes
135                            if ((size & 1) == 1)
136                            {
137                                    size++;
138                            }
139                            //System.out.println("Chunk " + getChunkName(magic) + ", size=" + size);
140                            switch(magic)
141                            {
142                                    case(MAGIC_BMHD): // main header with width, height, bit depth
143                                    {
144                                            if (hasBMHD)
145                                            {
146                                                    throw new InvalidFileStructureException("Error in " +
147                                                            "IFF file: more than one BMHD chunk.");
148                                            }
149                                            if (size != SIZE_BMHD)
150                                            {
151                                                    throw new InvalidFileStructureException("Cannot " +
152                                                            "load image. The bitmap header chunk does not " +
153                                                            "have the expected size.");
154                                            }
155                                            // image resolution in pixels
156                                            width = in.readShort();
157                                            height = in.readShort();
158                                            if (width < 1 || height < 1)
159                                            {
160                                                    throw new InvalidFileStructureException("Cannot " +
161                                                            "load image. The IFF file's bitmap header " +
162                                                            "contains invalid width and height values: " + 
163                                                            width + ", " + height);
164                                            }
165                                            // next four bytes don't matter
166                                            in.skipBytes(4);
167                                            // color depth, 1..8 or 24
168                                            numPlanes = in.readByte();
169                                            if ((numPlanes != 24) && (numPlanes < 1 || numPlanes > 8))
170                                            {
171                                                    throw new UnsupportedTypeException("Cannot load " +
172                                                            "image, unsupported number of bits per pixel: " + 
173                                                            numPlanes);
174                                            }
175                                            //System.out.println("\nnum planes=" + numPlanes);
176                                            masking = in.readByte();
177                                            // compression type, must be 0 or 1
178                                            compression = in.readByte();
179                                            if (compression != COMPRESSION_NONE && 
180                                                compression != COMPRESSION_RLE)
181                                            {
182                                                    throw new UnsupportedTypeException("Cannot load " +
183                                                            "image, unsupported compression type: " + 
184                                                            compression);
185                                            }
186                                            //System.out.println(getCompressionName(compression));
187                                            in.skipBytes(9);
188                                            hasBMHD = true;
189                                            break;
190                                    }
191                                    case(MAGIC_BODY):
192                                    {
193                                            if (!hasBMHD)
194                                            {
195                                                    // width still has its initialization value -1; no 
196                                                    // bitmap chunk was encountered
197                                                    throw new InvalidFileStructureException("Cannot load image. Error in " +
198                                                            "IFF input stream: No bitmap header chunk " +
199                                                            "encountered before image body chunk.");
200                                            }
201                                            if (palette == null && (!rgb24))
202                                            {
203                                                    // a missing color map is allowed only for truecolor images
204                                                    throw new InvalidFileStructureException("Cannot load image. Error in " +
205                                                            "IFF input stream: No colormap chunk " +
206                                                            "encountered before image body chunk.");
207                                            }
208                                            result = loadImage(in);
209                                            break;
210                                    }
211                                    case(MAGIC_CAMG):
212                                    {
213                                            if (hasCAMG)
214                                            {
215                                                    throw new InvalidFileStructureException("Cannot load image. Error in " +
216                                                            "IFF input stream: More than one CAMG chunk.");
217                                            }
218                                            hasCAMG = true;
219                                            if (size < 4)
220                                            {
221                                                    throw new InvalidFileStructureException("Cannot load" +
222                                                            " image. CAMG must be at least four bytes large; " +
223                                                            "found: " + size);
224                                            }
225                                            camg = in.readInt();
226                                            ham = (camg & 0x800) != 0;
227                                            ehb = (camg & 0x80) != 0;
228                                            //System.out.println("ham=" + ham);
229                                            in.skipBytes(size - 4);
230                                            break;
231                                    }
232                                    case(MAGIC_CMAP): // palette (color map)
233                                    {
234                                            if (palette != null)
235                                            {
236                                                    throw new InvalidFileStructureException("Cannot " +
237                                                            "load image. Error in IFF " +
238                                                            "input stream: More than one palette.");
239                                            }
240                                            if (size < 3 || (size % 3) != 0)
241                                            {
242                                                    throw new InvalidFileStructureException("Cannot " +
243                                                            "load image. The size of the colormap is " +
244                                                            "invalid: " + size);
245                                            }
246                                            int numColors = size / 3;
247                                            palette = new Palette(numColors, 255);
248                                            for (int i = 0; i < numColors; i++)
249                                            {
250                                                    palette.putSample(Palette.INDEX_RED, i, in.readByte() & 0xff);
251                                                    palette.putSample(Palette.INDEX_GREEN, i, in.readByte() & 0xff);
252                                                    palette.putSample(Palette.INDEX_BLUE, i, in.readByte() & 0xff);
253                                            }
254                                            break;
255                                    }
256                                    default:
257                                    {
258                                            if (in.skipBytes(size) != size)
259                                            {
260                                                    throw new IOException("Error skipping " + size +
261                                                            " bytes of input stream.");
262                                            }
263                                            break;
264                                    }
265                            }
266                    }
267                    while(result == null);
268                    setImage(result);
269            }
270    
271            /**
272             * Converts input planes to index or truecolor output values.
273             * Exact interpretation depends on the type of ILBM image storage:
274             * <ul>
275             * <li>normal mode; the 1 to 8 planes create index values which are used
276             *  with the colormap</li>
277             * <li>RGB24; each of the 24 planes adds one bit to the three intensity
278             *  values for red, green and blue; no color map is necessary</li>
279             * <li>HAM6; a six bit integer (0 to 63) is assembled from the planes
280             *  and the top two bits determine if the previous color is modified or
281             *  if the lower four bits are used as an index into the palette (which
282             *  has consequently 2<sup>4</sup> = 16 entries</li>
283             * </ul>
284             * @param sourcePlanes
285             * @param dest
286             */
287            private void convertRow(byte[][] sourcePlaneData, byte[][] dest)
288            {
289                    int sourceMask = 0x80;
290                    int sourceIndex = 0;
291                    int lastRed = 0;
292                    int lastGreen = 0;
293                    int lastBlue = 0;
294                    for (int x = 0; x < width; x++)
295                    {
296                            int destMask = 1;
297                            int index = 0;
298                            for (int p = 0; p < sourcePlaneData.length; p++)
299                            {
300                                    if ((sourcePlaneData[p][sourceIndex] & sourceMask) != 0)
301                                    {
302                                            index |= destMask;
303                                    }
304                                    destMask <<= 1;
305                            }
306                            if ((x & 7) == 7)
307                            {
308                                    sourceIndex++;
309                            }
310                            if (sourceMask == 0x01)
311                            {
312                                    sourceMask = 0x80;
313                            }
314                            else
315                            {
316                                    sourceMask >>= 1;
317                            }
318                            if (ham6)
319                            {
320                                    //System.out.println("enter ham6");
321                                    int paletteIndex = index & 0x0f;
322                                    //System.out.println("palette index=" + paletteIndex);
323                                    switch((index >> 4) & 0x03)
324                                    {
325                                            case(0): // HOLD
326                                            {
327                                                    lastRed = palette.getSample(Palette.INDEX_RED, paletteIndex);
328                                                    lastGreen = palette.getSample(Palette.INDEX_GREEN, paletteIndex);
329                                                    lastBlue = palette.getSample(Palette.INDEX_BLUE, paletteIndex);
330                                                    break;
331                                            }
332                                            case(1): // MODIFY BLUE
333                                            {
334                                                    lastBlue = (lastBlue & 0x0f) | (paletteIndex << 4);
335                                                    break;
336                                            }
337                                            case(2): // MODIFY RED
338                                            {
339                                                    lastRed = (lastRed & 0x0f) | (paletteIndex << 4);
340                                                    break;
341                                            }
342                                            case(3): // MODIFY GREEN
343                                            {
344                                                    lastGreen = (lastGreen & 0x0f) | (paletteIndex << 4);
345                                                    break;
346                                            }
347                                    }
348                                    dest[0][x] = (byte)lastRed;
349                                    dest[1][x] = (byte)lastGreen;
350                                    dest[2][x] = (byte)lastBlue;
351                            }
352                            else
353                            if (ham8)
354                            {
355                                    int paletteIndex = index & 0x3f;
356                                    //System.out.println("palette index=" + paletteIndex);
357                                    switch((index >> 6) & 0x03)
358                                    {
359                                            case(0): // HOLD
360                                            {
361                                                    lastRed = palette.getSample(Palette.INDEX_RED, paletteIndex);
362                                                    lastGreen = palette.getSample(Palette.INDEX_GREEN, paletteIndex);
363                                                    lastBlue = palette.getSample(Palette.INDEX_BLUE, paletteIndex);
364                                                    break;
365                                            }
366                                            case(1): // MODIFY BLUE
367                                            {
368                                                    lastBlue = (lastBlue & 0x03) | (paletteIndex << 2);
369                                                    break;
370                                            }
371                                            case(2): // MODIFY RED
372                                            {
373                                                    lastRed = (lastRed & 0x03) | (paletteIndex << 2);
374                                                    break;
375                                            }
376                                            case(3): // MODIFY GREEN
377                                            {
378                                                    lastGreen = (lastGreen & 0x03) | (paletteIndex << 2);
379                                                    break;
380                                            }
381                                    }
382                                    dest[0][x] = (byte)lastRed;
383                                    dest[1][x] = (byte)lastGreen;
384                                    dest[2][x] = (byte)lastBlue;
385                            }
386                            else
387                            if (rgb24)
388                            {
389                                    dest[2][x] = (byte)(index >> 16);
390                                    dest[1][x] = (byte)(index >> 8);
391                                    dest[0][x] = (byte)index;
392                            }
393                            else
394                            {
395                                    /* the value is an index into the lookup table */
396                                    //destRgbData[destOffset++] = rgbLookup[index];
397                                    dest[0][x] = (byte)index;
398                            }
399                    }
400            }
401    
402            private void createExtraHalfbritePalette()
403            {
404                    if (palette == null)
405                    {
406                            return;
407                    }
408                    int numPaletteEntries = palette.getNumEntries();
409                    Palette tempPalette = new Palette(numPaletteEntries * 2, 255);
410                    for (int i = 0; i < numPaletteEntries; i++)
411                    {
412                            int red = palette.getSample(Palette.INDEX_RED, i);
413                            tempPalette.putSample(Palette.INDEX_RED, numPaletteEntries + i, red);
414                            tempPalette.putSample(Palette.INDEX_RED, i, (red / 2) & 0xf0);
415                            int green = palette.getSample(Palette.INDEX_GREEN, i);
416                            tempPalette.putSample(Palette.INDEX_GREEN, numPaletteEntries + i, red);
417                            tempPalette.putSample(Palette.INDEX_GREEN, i, (green / 2) & 0xf0);
418                            int blue = palette.getSample(Palette.INDEX_BLUE, i);
419                            tempPalette.putSample(Palette.INDEX_BLUE, numPaletteEntries + i, blue);
420                            tempPalette.putSample(Palette.INDEX_BLUE, i, (blue / 2) & 0xf0);
421                    }
422                    palette = tempPalette;
423            }
424    
425            private static String getChunkName(int name)
426            {
427                    StringBuffer sb = new StringBuffer(4);
428                    sb.setLength(4);
429                    sb.setCharAt(0, (char)((name >> 24) & 0xff));
430                    sb.setCharAt(1, (char)((name >> 16) & 0xff));
431                    sb.setCharAt(2, (char)((name >> 8) & 0xff));
432                    sb.setCharAt(3, (char)((name & 0xff)));
433                    return new String(sb);
434            }
435    
436            private static String getCompressionName(byte method)
437            {
438                    switch(method)
439                    {
440                            case(COMPRESSION_NONE): return "Uncompressed";
441                            case(COMPRESSION_RLE): return "RLE";
442                            default: return "Unknown method (" + (method & 0xff) + ")";
443                    }
444            }
445    
446            public String[] getFileExtensions()
447            {
448                    return new String[] {".lbm", ".iff"};
449            }
450    
451            public String getFormatName()
452            {
453                    return "Amiga Interchange File Format (IFF, LBM)";
454            }
455    
456            public String[] getMimeTypes()
457            {
458                    return new String[] {"image/x-iff"};
459            }
460    
461            public boolean isLoadingSupported()
462            {
463                    return true;
464            }
465    
466            public boolean isSavingSupported()
467            {
468                    return false;
469            }
470    
471            /**
472             * Loads data.length bytes from the input stream to the data array, 
473             * regarding the compression type.
474             * COMPRESSION_NONE will make this method load data.length bytes from
475             * the input stream.
476             * COMPRESSION_RLE will make this method decompress data.length bytes
477             * from input.
478             */
479            private void loadBytes(DataInput in, byte[] data, int num, int y) throws
480                    InvalidFileStructureException, 
481                    IOException
482            {
483                    switch(compression)
484                    {
485                            case(COMPRESSION_NONE):
486                            {
487                                    in.readFully(data, 0, num);
488                                    break;
489                            }
490                            case(COMPRESSION_RLE):
491                            {
492                                    int x = 0;
493                                    while (x < num)
494                                    {
495                                            int n = in.readByte() & 0xff;
496                                            //System.out.println("value=" + n);
497                                            boolean compressed = false;
498                                            int count = -1;
499                                            try
500                                            {
501                                                    if (n < 128)
502                                                    {
503                                                            // copy next n + 1 bytes literally
504                                                            n++;
505                                                            in.readFully(data, x, n);
506                                                            x += n;
507                                                    }
508                                                    else
509                                                    {
510                                                            // if n == -128, nothing happens
511                                                            if (n > 128)
512                                                            {
513                                                                    compressed = true;
514                                                                    // otherwise, compute counter
515                                                                    count = 257 - n;
516                                                                    // read another byte
517                                                                    byte value = in.readByte();
518                                                                    // write this byte counter times to output
519                                                                    while (count-- > 0)
520                                                                    {
521                                                                            data[x++] = value;
522                                                                    }
523                                                            }
524                                                    }
525                                            }
526                                            catch (ArrayIndexOutOfBoundsException ioobe)
527                                            {
528                                                    //System.out.println("Loading error");
529                                                    /* if the encoder did anything wrong, the above code
530                                                       could potentially write beyond array boundaries
531                                                       (e.g. if runs of data exceed line boundaries);
532                                                       this would result in an ArrayIndexOutOfBoundsException
533                                                       thrown by the virtual machine;
534                                                       to give a more understandable error message to the 
535                                                       user, this exception is caught here and a
536                                                       explanatory InvalidFileStructureException is thrown */
537                                                    throw new InvalidFileStructureException("Error: " +
538                                                            "RLE-compressed image " +
539                                                            "file seems to be corrupt (compressed=" + compressed +
540                                                            ", x=" + x + ", y=" + y +
541                                                            ", count=" + (compressed ? (-((int)n) + 1) : n) + 
542                                                            ", array length=" + data.length + ").");
543                                            }
544                                    }
545                                    break;
546                            }
547                            default:
548                            {
549                                    throw new InvalidFileStructureException("Error loading " +
550                                            "image; unknown compression type (" + compression + ")");
551                            }
552                    }
553            }
554    
555            /**
556             * Loads an image from given input stream in, regarding the compression
557             * type. The image will have 1 to 8 or 24 planes, a resolution given by
558             * the dimension width times height. The color map data will be used to
559             * convert index values to RGB pixels.
560             * Returns the resulting image.
561             * Will throw an IOException if either there were errors reading from the
562             * input stream or if the file does not exactly match the file format.
563             */
564            private PixelImage loadImage(DataInput in) throws
565                    InvalidFileStructureException, 
566                    IOException, 
567                    UnsupportedTypeException,
568                    WrongParameterException
569            {
570                    setBoundsIfNecessary(width, height);
571                    checkImageResolution();
572                    if (ham)
573                    {
574                            if (numPlanes == 6)
575                            {
576                                    ham6 = true;
577                            }
578                            else
579                            if (numPlanes == 8)
580                            {
581                                    ham8 = true;
582                            }
583                            else
584                            {
585                                    throw new UnsupportedTypeException("Cannot handle " +
586                                            "IFF ILBM HAM image file with number of planes " +
587                                            "other than 6 or 8 (got " + numPlanes + ").");
588                            }
589                            if (palette == null)
590                            {
591                                    throw new InvalidFileStructureException("Invalid IFF ILBM " +
592                                            "file: HAM (Hold And Modify) image without a palette.");
593                            }
594                            int numPaletteEntries = palette.getNumEntries();
595                            if (ham6 && numPaletteEntries < 16)
596                            {
597                                    throw new InvalidFileStructureException("Invalid IFF ILBM " +
598                                            "file: HAM (Hold And Modify) 6 bit image with a " +
599                                            "number of palette entries less than 16 (" +
600                                            numPaletteEntries + ").");
601                            }
602                            if (ham8 && numPaletteEntries < 64)
603                            {
604                                    throw new InvalidFileStructureException("Invalid IFF ILBM " +
605                                            "file: HAM (Hold And Modify) 8 bit image with a " +
606                                            "number of palette entries less than 64 (" +
607                                            numPaletteEntries + ").");
608                            }
609                    }
610                    if (ehb)
611                    {
612                            createExtraHalfbritePalette();
613                    }
614                    int numBytesPerPlane = (width + 7) / 8;
615                    PixelImage image = null;
616                    Paletted8Image palettedImage = null;
617                    RGB24Image rgbImage = null;
618                    if (numPlanes == 24 || ham)
619                    {
620                            rgbImage = new MemoryRGB24Image(getBoundsWidth(), getBoundsHeight());
621                            image = rgbImage;
622                    }
623                    else
624                    {
625                            palettedImage = new MemoryPaletted8Image(getBoundsWidth(), getBoundsHeight(), palette);
626                            image = palettedImage;
627                    }
628                    /* only matters for uncompressed files;
629                       will be true if the number of bytes is odd;
630                       is computed differently for PBM and ILBM types
631                    */
632                    boolean oddBytesPerRow = (((numBytesPerPlane * numPlanes) % 2) != 0);
633                    if (type == MAGIC_PBM)
634                    {
635                            oddBytesPerRow = ((width % 2) == 1);
636                    }
637                    // plane data will have numPlanes planes for ILBM and 1 plane for PBM
638                    byte[][] planes = null;
639                    int numChannels = 1;
640                    
641                    if (type == MAGIC_ILBM)
642                    {
643                            int allocBytes = numBytesPerPlane;
644                            if ((numBytesPerPlane % 2) == 1)
645                            {
646                                    allocBytes++;
647                            }
648                            // allocate numPlanes byte arrays
649                            planes = new byte[numPlanes][];
650                            if (rgb24 || ham)
651                            {
652                                    numChannels = 3;
653                            }
654                            // for each of these byte arrays allocate numBytesPerPlane bytes
655                            for (int i = 0; i < numPlanes; i++)
656                            {
657                                    planes[i] = new byte[allocBytes];
658                            }
659                    }
660                    else
661                    {
662                            // only one plane, but each plane has width bytes instead of 
663                            // numBytesPerPlane
664                            planes = new byte[1][];
665                            planes[0] = new byte[width];
666                    }
667                    byte[][] dest = new byte[numChannels][];
668                    for (int i = 0; i < numChannels; i++)
669                    {
670                            dest[i] = new byte[width];
671                    }
672                    /*System.out.println("numChannels=" + numChannels + ", width=" + width + 
673                            "dest.length=" + dest.length + "dest[0].length" + dest[0].length);*/
674                    int pixelOffset = 0;
675                    // load all rows
676                    int x1 = getBoundsX1();
677                    for (int y = 0, destY = 0 - getBoundsY1(); y <= getBoundsY2(); y++, destY++)
678                    {
679                            // load one row, different approach for PBM and ILBM
680                            if (type == MAGIC_ILBM)
681                            {
682                                    // decode all planes for a complete row
683                                    for (int p = 0; p < numPlanes; p++)
684                                    {
685                                            loadBytes(in, planes[p], numBytesPerPlane, y);
686                                    }
687                            }
688                            else
689                            if (type == MAGIC_PBM)
690                            {
691                                    loadBytes(in, planes[0], numBytesPerPlane, y);
692                            }
693                            /* all uncompressed rows must have an even number of bytes
694                               so in case the number of bytes per row is odd, one byte
695                               is read and dropped */
696                            if (compression == COMPRESSION_NONE && oddBytesPerRow)
697                            {
698                                    in.readByte();
699                            }
700                            setProgress(y, getBoundsY2() + 1);
701                            // if we do not need the row we just loaded we continue loading
702                            // the next row
703                            if (!isRowRequired(y))
704                            {
705                                    continue;
706                            }
707                            //System.out.println("storing row " + y + " as " + destY + ", numPlanes="+ numPlanes + ",type=" + type);
708                            // compute offset into pixel data array
709                            if (type == MAGIC_ILBM)
710                            {
711                                    convertRow(planes, dest);
712                                    if (rgb24 || ham)
713                                    {
714                                            rgbImage.putByteSamples(RGB24Image.INDEX_RED, 0, destY, 
715                                                    getBoundsWidth(), 1, dest[0], getBoundsX1());
716                                            rgbImage.putByteSamples(RGB24Image.INDEX_GREEN, 0, destY, 
717                                                    getBoundsWidth(), 1, dest[1], getBoundsX1());
718                                            rgbImage.putByteSamples(RGB24Image.INDEX_BLUE, 0, destY, 
719                                                    getBoundsWidth(), 1, dest[2], getBoundsX1());
720                                    }
721                                    else
722                                    {
723                                            palettedImage.putByteSamples(0, 0, destY, 
724                                                    getBoundsWidth(), 1, dest[0], getBoundsX1());
725                                    }
726                            }
727                            else
728                            if (type == MAGIC_PBM)
729                            {
730                                    palettedImage.putByteSamples(0, 0, destY, getBoundsWidth(), 1,
731                                            planes[0], getBoundsX1());
732                            }
733                    }
734                    return image;
735            }
736    
737            public void process() throws 
738                    InvalidFileStructureException, 
739                    MissingParameterException, 
740                    OperationFailedException,
741                    UnsupportedTypeException, 
742                    WrongFileFormatException
743            {
744                    initModeFromIOObjects();
745                    if (getMode() == CodecMode.LOAD)
746                    {
747                            try
748                            {
749                                    checkAndLoad();
750                            }
751                            catch (IOException ioe)
752                            {
753                                    throw new InvalidFileStructureException("I/O error while loading: " + ioe.toString());
754                            }
755                    }
756                    else
757                    {
758                            throw new OperationFailedException("Only loading from IFF is supported.");
759                    }
760            }
761    }