KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > openlaszlo > iv > flash > util > GIFHelper


1 /*
2  * $Id: GIFHelper.java,v 1.2 2002/02/15 23:44:28 skavish Exp $
3  *
4  * ===========================================================================
5  *
6  * The JGenerator Software License, Version 1.0
7  *
8  * Copyright (c) 2000 Dmitry Skavish (skavish@usa.net). All rights reserved.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions are met:
12  *
13  * 1. Redistributions of source code must retain the above copyright
14  * notice, this list of conditions and the following disclaimer.
15  *
16  * 2. Redistributions in binary form must reproduce the above copyright
17  * notice, this list of conditions and the following disclaimer in
18  * the documentation and/or other materials provided with the
19  * distribution.
20  *
21  * 3. The end-user documentation included with the redistribution, if
22  * any, must include the following acknowlegement:
23  * "This product includes software developed by Dmitry Skavish
24  * (skavish@usa.net, http://www.flashgap.com/)."
25  * Alternately, this acknowlegement may appear in the software itself,
26  * if and wherever such third-party acknowlegements normally appear.
27  *
28  * 4. The name "The JGenerator" must not be used to endorse or promote
29  * products derived from this software without prior written permission.
30  * For written permission, please contact skavish@usa.net.
31  *
32  * 5. Products derived from this software may not be called "The JGenerator"
33  * nor may "The JGenerator" appear in their names without prior written
34  * permission of Dmitry Skavish.
35  *
36  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
37  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
38  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
39  * DISCLAIMED. IN NO EVENT SHALL DMITRY SKAVISH OR THE OTHER
40  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
41  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
42  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
43  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
44  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
45  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
46  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
47  * SUCH DAMAGE.
48  *
49  */

50
51 package org.openlaszlo.iv.flash.util;
52
53 import java.io.*;
54 import java.util.zip.Deflater JavaDoc;
55 import org.openlaszlo.iv.flash.parser.DataMarker;
56
57 /**
58  * GIF Reader utility class
59  * Parse GIF code from an inputStream without using java.awt
60  * (to avoid the need of a display on a server)
61  *
62  * Use a modified version of the GIFDecoder part the PJA Toolkit
63  * Part of the implementation has been commented because we didn't need it
64  * Basically everything that was dealing with GIF comments output
65  * and the creation of an RGB image
66  *
67  * With kind permission of Emmanuel PUYBARET from eteks
68  * Lots of contributions here, but the code is reliable and approved
69  *
70  * Following is the initial copyright notice (which for this project is Apache style)
71  *
72  * ****************************************************************************
73  *
74  * Copyright (c) 2000 Emmanuel PUYBARET / eTeks <info@eteks.com>. All Rights Reserved.
75  *
76  * Raster over-run errors fixed by Alan Dix (alan@hcibook.com www.hiraeth.com/alan)
77  *
78  * Visit eTeks web site for up-to-date versions of the original file and other
79  * Java tools and tutorials : http://www.eteks.com/
80  *
81  * ****************************************************************************
82  *
83  * This file is based and translated from the following C source file :
84  *
85  * xvgif.c - GIF loading code for 'xv'. Based strongly on...
86  *
87  * gif2ras.c - Converts from a Compuserve GIF (tm) image to a Sun raster image.
88  *
89  * Copyright (c) 1988, 1989 by Patrick J. Naughton
90  *
91  * Author: Patrick J. Naughton
92  * naughton@wind.sun.com
93  *
94  * Permission to use, copy, modify, and distribute this software and its
95  * documentation for any purpose and without fee is hereby granted,
96  * provided that the above copyright notice appear in all copies and that
97  * both that copyright notice and this permission notice appear in
98  * supporting documentation.
99  *
100  * This file is provided AS IS with no warranties of any kind. The author
101  * shall have no liability with respect to the infringement of copyrights,
102  * trade secrets or any patents by this file or any part thereof. In no
103  * event will the author be liable for any lost revenue or profits or
104  * other special, indirect and consequential damages.
105  *
106  * ****************************************************************************
107  *
108  */

109
110 public class GIFHelper
111 {
112
113     private byte bytePixels []; // image data in index model
114
private int width; // image width
115
private int height; // image height
116
private int bitMask; // size of the colormap - 1;
117
private int colorMapSize; // actual size of the colormap
118

119     private InputStream input; // the GIF ByteArrayInputStream
120
private byte data[]; // the compressed (zlib) byte array
121

122
123 /******************************************************************************
124  * The following methods are the getters needed for JGen to process
125  * a new LBitmap object to insert into the Script
126  * Added to the original GIFReader especially for JGen
127  ******************************************************************************/

128
129     public int getColorTableSize() {
130         return bitMask;
131     }
132
133     public int getWidth() {
134         return width;
135     }
136
137     public int getHeight() {
138         return height;
139     }
140
141     public DataMarker getZlibData() throws IOException {
142         int deflatedSize = deflate(); // compress the data
143
return new DataMarker(data,0,deflatedSize);
144     }
145
146     public boolean isAlpha() {
147         return (transparentIndex > -1);
148     }
149
150     /**
151      * Launch the loadGIF routine
152      *
153      * @param buffer
154      * @exception IOException
155      */

156     public void doRead( byte[] buffer ) throws IOException {
157         doRead( new ByteArrayInputStream(buffer, 0, buffer.length) );
158     }
159
160     public void doRead( FlashBuffer fob ) throws IOException {
161         doRead( fob.getInputStream() );
162     }
163
164     public void doRead( InputStream input ) throws IOException {
165         this.input = input;
166         try {
167             loadGIF(input);
168         } finally {
169             try {
170                 input.close();
171             } catch (IOException ioe) {}
172         }
173     }
174
175     /**
176      * Performs the ZLib compression.<br>
177      *
178      * <p>Everything from here is the private implementation of the reader
179      * But we don't want to disturb other classes with that stuff, do we ?
180      *
181      * @return
182      * @exception IOException
183      */

184     private int deflate() throws IOException {
185         int retour = 0;
186         int plus = 0;
187         if (transparentIndex > -1)
188             plus++;
189         int maxpixels = bytePixels.length;
190         int falsewidth = width;
191         int added = 0;
192         if (width % 4 > 0)
193         {
194             while (falsewidth % 4 > 0)
195             {
196                 falsewidth++;
197                 added++;
198             }
199             maxpixels = falsewidth * height;
200         }
201         byte[] map = new byte[colorMapSize*(3+plus) + maxpixels];
202         for (int i = 0; i < colorMapSize; i++)
203         {
204             if (transparentIndex > -1)
205             {
206                 if (transparentIndex == i)
207                 {
208                     map[i*(3+plus)] = 0;
209                     map[(i*(3+plus))+1] = 0;
210                     map[(i*(3+plus))+2] = 0;
211                     map[(i*(3+plus))+3] = 0;
212                 }
213                 else
214                 {
215                     map[i*(3+plus)] = r[i];
216                     map[(i*(3+plus))+1] = g[i];
217                     map[(i*(3+plus))+2] = b[i];
218                     map[(i*(3+plus))+3] = (byte) 255;
219                 }
220             }
221             else
222             {
223                 map[i*(3+plus)] = r[i];
224                 map[(i*(3+plus))+1] = g[i];
225                 map[(i*(3+plus))+2] = b[i];
226             }
227         }
228         if (width % 4 > 0)
229         {
230             byte[] tempPixels = new byte[maxpixels];
231             int idTemp = 0;
232             int idByte = 0;
233             for (int h = 0; h < height; h++)
234             {
235                 for (int w = 0; w < width; w++)
236                     tempPixels[idTemp++] = bytePixels[idByte++];
237                 for (int i = 0; i < added; i++)
238                     tempPixels[idTemp++] = 0x00;
239             }
240             bytePixels = tempPixels;
241         }
242         System.arraycopy(bytePixels,0,map,(colorMapSize*(3+plus)),bytePixels.length);
243         Deflater JavaDoc def = new Deflater JavaDoc();
244         def.setInput(map);
245         data = new byte[(colorMapSize*(3+plus))+bytePixels.length];
246         def.finish();
247         retour = def.deflate(data);
248         return retour;
249     }
250
251 /******************************************************************************
252  * This is where the original GIFReader from PJA starts
253  * A few things had been commented out because it wasn't needed
254  ******************************************************************************/

255
256     // Where the xvgif.c translation starts
257

258     private int bitOffset = 0; // Bit Offset of next code
259
private int XC = 0; // Output X coords of current pixel
260
private int YC = 0; // Output Y coords of curent pixel
261
private int pass = 0; // Used by output routine if interlaced pixels
262
private int ptr = 0; // Just an index
263
private int oldYC = -1; // Here to remember YC
264

265     private byte r [] = new byte [256]; // red colormap value
266
private byte g [] = new byte [256]; // green colormap value
267
private byte b [] = new byte [256]; // blue colormap, value
268
private int transparentIndex = -1; // index of the transparent color in the index array
269

270   // private int intPixels []; // image data in RGB model
271
// we don't really need this one, since we only compute the bytePixels Array
272

273   // private String fullInfo; // Format: field in info box
274
// private String shortInfo; // short format info
275
// private String comment; // comment text
276

277     private final static String JavaDoc id87 = "GIF87a";
278     private final static String JavaDoc id89 = "GIF89a";
279
280     private final static short EGA_PALETTE [][] = {
281         {0,0,0}, {0,0,128}, {0,128,0}, {0,128,128},
282         {128,0,0}, {128,0,128}, {128,128,0}, {200,200,200},
283         {100,100,100}, {100,100,255}, {100,255,100}, {100,255,255},
284         {255,100,100}, {255,100,255}, {255,255,100}, {255,255,255} };
285
286     private final static byte EXTENSION = 0x21;
287     private final static byte IMAGESEP = 0x2c;
288     private final static byte TRAILER = 0x3b;
289     private final static byte INTERLACEMASK = 0x40;
290     private final static byte COLORMAPMASK = (byte)0x80;
291
292     private void loadGIF (InputStream input) throws IOException
293     {
294         if (!(input instanceof BufferedInputStream))
295             input = new BufferedInputStream (input);
296
297         // Use a DataInputStream to have EOFException if file is corrupted
298
DataInputStream dataInput = new DataInputStream (input);
299
300         // initialize variables
301
bitOffset = XC = YC = pass = 0;
302         boolean gotimage = false;
303         boolean gif89 = false;
304
305         byte [] idBytes = new byte [6];
306         for (int i = 0; i < 6; i++)
307             idBytes [i] = dataInput.readByte ();
308
309         String JavaDoc id = new String JavaDoc (idBytes);
310         if (id.equals (id87))
311             gif89 = false;
312         else if (id.equals (id89))
313             gif89 = true;
314         else
315             warning (input, "not a GIF file");
316
317         // Get variables from the GIF screen descriptor
318
byte aByte = dataInput.readByte ();
319         int screenWidth = ((int)aByte & 0xFF) + 0x100 * (dataInput.readByte () & 0xFF); // screen dimensions... not used.
320
aByte = dataInput.readByte ();
321         int screenHeight = ((int)aByte & 0xFF) + 0x100 * (dataInput.readByte () & 0xFF);
322
323         aByte = dataInput.readByte ();
324         boolean hasColormap = (aByte & COLORMAPMASK) != 0;
325
326         // Bits per pixel, read from GIF header
327
int bitsPerPixel = (aByte & 7) + 1;
328         // Number of colors
329
colorMapSize = 1 << bitsPerPixel;
330         // AND mask for data size
331
bitMask = colorMapSize - 1;
332
333         int background = dataInput.readByte () & 0xFF; // background color... not used.
334

335         int aspect = dataInput.readByte () & 0xFF;
336         if (aspect != 0)
337             if (!gif89)
338                 warning (input, "corrupt GIF file (screen descriptor)");
339
340         // Read in global colormap.
341
if (hasColormap)
342             for (int i = 0; i < colorMapSize; i++)
343             {
344                 r [i] = dataInput.readByte ();
345                 g [i] = dataInput.readByte ();
346                 b [i] = dataInput.readByte ();
347             }
348         else
349         {
350             colorMapSize = 256;
351             bitMask = 255;
352             // no colormap in GIF file
353
// put std EGA palette (repeated 16 times) into colormap, for lack of
354
// anything better to do
355
for (int i = 0; i < 256; i++)
356             {
357                 r [i] = (byte)EGA_PALETTE [i & 15][0];
358                 g [i] = (byte)EGA_PALETTE [i & 15][1];
359                 b [i] = (byte)EGA_PALETTE [i & 15][2];
360             }
361         }
362
363         for (int block = 0;
364            (block = dataInput.readByte () & 0xFF) != TRAILER; )
365         if (block == EXTENSION)
366         {
367             // possible things at this point are:
368
// an application extension block
369
// a comment extension block
370
// an (optional) graphic control extension block
371
// followed by either an image
372
// or a plaintext extension
373

374             // parse extension blocks
375
int fn, blocksize, aspnum, aspden;
376
377             // read extension block
378
fn = dataInput.readByte () & 0xFF;
379
380             if (fn == 'R')
381             {
382                 // GIF87 aspect extension
383
int blockSize;
384
385                 blocksize = dataInput.readByte () & 0xFF;
386                 if (blocksize == 2)
387                 {
388                     aspnum = dataInput.readByte ();
389                     aspden = dataInput.readByte ();
390                     if (aspden <= 0 || aspnum <= 0)
391                     {
392                         aspnum =
393                         aspden = 1;
394                     }
395                 }
396                 else
397                     dataInput.skipBytes (blocksize);
398
399                 while ((blockSize = dataInput.readByte () & 0xFF) > 0)
400                     // eat any following data subblocks
401
dataInput.skipBytes (blockSize);
402             }
403             else if (fn == 0xFE)
404             {
405                 // Comment Extension
406
// We don't really need them, so let's eat that too :
407
int blockSize;
408                 while ((blockSize = dataInput.readByte () & 0xFF) > 0)
409                     dataInput.skipBytes (blockSize);
410
411                 /* This part was in the original GIFReader but we don't use the GIF comments
412                 // to read the comments :
413                 for (int blockSize = 0; (blockSize = dataInput.readByte () & 0xFF) != 0; )
414                 {
415                     byte commentBytes [] = new byte [blockSize];
416                     for (int i = 0; i < blockSize; i++)
417                         commentBytes [i] = dataInput.readByte ();
418
419                     if (comment != null)
420                         comment += "\n" + new String (commentBytes);
421                     else
422                         comment = new String (commentBytes);
423                 }
424                 */

425             }
426             else if (fn == 0x01)
427             {
428                 // PlainText Extension
429
int blockSize = dataInput.readByte () & 0xFF;
430                 int tgLeft = dataInput.readByte () & 0xFF;
431                 tgLeft += (dataInput.readByte () & 0xFF) << 8;
432                 int tgTop = dataInput.readByte () & 0xFF;
433                 tgTop += (dataInput.readByte () & 0xFF) << 8;
434                 int tgWidth = dataInput.readByte () & 0xFF;
435                 tgWidth += (dataInput.readByte () & 0xFF) << 8;
436                 int tgHeight = dataInput.readByte () & 0xFF;
437                 tgHeight += (dataInput.readByte () & 0xFF) << 8;
438                 int cWidth = dataInput.readByte () & 0xFF;
439                 int cHeight = dataInput.readByte () & 0xFF;
440                 int fg = dataInput.readByte () & 0xFF;
441                 int bg = dataInput.readByte () & 0xFF;
442
443                 dataInput.skipBytes (blockSize - 12); // read rest of first subblock
444

445                 // read (and ignore) data sub-blocks
446
while ((blockSize = dataInput.readByte () & 0xFF) != 0)
447                   dataInput.skipBytes (blockSize);
448             }
449             else if (fn == 0xF9)
450             {
451                 // Graphic Control Extension
452
for (int blockSize = 0;
453                     (blockSize = dataInput.readByte () & 0xFF) != 0; )
454                 // Added transparent GIF management here
455
if (blockSize == 4)
456                 {
457                     int ext1 = (dataInput.readByte () & 0xFF);
458                     int ext2 = (dataInput.readByte () & 0xFF);
459                     int ext3 = (dataInput.readByte () & 0xFF);
460                     int ext4 = (dataInput.readByte () & 0xFF);
461
462                     // v2.1.1 Changed condition for transparent GIFs
463
if ((ext1 & 0x01) != 0)
464                         transparentIndex = ext4;
465                 }
466                 else
467                     dataInput.skipBytes (blockSize);
468             }
469             else if (fn == 0xFF)
470             {
471                 // Application Extension
472
// read (and ignore) data sub-blocks
473
for (int blockSize = 0;
474                     (blockSize = dataInput.readByte () & 0xFF) != 0; )
475                 dataInput.skipBytes (blockSize);
476             }
477             else
478             {
479                 // unknown extension
480
// read (and ignore) data sub-blocks
481
for (int blockSize = 0;
482                     (blockSize = dataInput.readByte () & 0xFF) != 0; )
483                 dataInput.skipBytes (blockSize);
484             }
485         }
486         else if (block == IMAGESEP)
487         {
488             if (gotimage)
489             {
490                 // just skip over remaining images
491
dataInput.skipBytes (8); // left position
492
// top position
493
// width
494
// height
495
int misc = dataInput.readByte () & 0xFF; // misc. bits
496

497                 if ((misc & 0x80) != 0)
498                     // image has local colormap. skip it
499
for (int i = 0; i < 1 << ((misc & 7) + 1); i++)
500                         dataInput.skipBytes (3);
501
502                 dataInput.skipBytes (1); // minimum code size
503

504                 // skip image data sub-blocks
505
for (int blockSize = 0;
506                     (blockSize = dataInput.readByte () & 0xFF) != 0; )
507                 dataInput.skipBytes (blockSize);
508             }
509             else
510             {
511                 readImage (dataInput, bitsPerPixel, bitMask, hasColormap, gif89);
512                 gotimage = true;
513             }
514         }
515         else
516         {
517             // unknown block type
518
// don't mention bad block if file was trunc'd, as it's all bogus
519
String JavaDoc str = "Unknown block type (0x" + Integer.toString (block, 16) + ")";
520             warning (input, str);
521             break;
522         }
523
524         if (!gotimage)
525             warning (input, "no image data found in GIF file");
526     }
527
528     private void readImage (DataInputStream dataInput,
529                             int bitsPerPixel,
530                             int bitMask,
531                             boolean hasColormap,
532                             boolean gif89) throws IOException
533     {
534         int npixels = 0;
535         int maxpixels = 0;
536
537         // read in values from the image descriptor
538
byte aByte = dataInput.readByte ();
539         int leftOffset = (aByte & 0xFF) + 0x100 * (dataInput.readByte () & 0xFF);
540         aByte = dataInput.readByte ();
541         int topOffset = (aByte & 0xFF) + 0x100 * (dataInput.readByte () & 0xFF);
542         aByte = dataInput.readByte ();
543         width = (aByte & 0xFF) + 0x100 * (dataInput.readByte () & 0xFF);
544         aByte = dataInput.readByte ();
545         height = (aByte & 0xFF) + 0x100 * (dataInput.readByte () & 0xFF);
546
547         int misc = dataInput.readByte (); // miscellaneous bits (interlace, local cmap)
548
boolean interlace = (misc & INTERLACEMASK) != 0;
549
550         if ((misc & 0x80) != 0)
551             for (int i = 0; i < 1 << ((misc & 7) + 1); i++)
552             {
553                 r [i] = dataInput.readByte ();
554                 g [i] = dataInput.readByte ();
555                 b [i] = dataInput.readByte ();
556             }
557
558
559         if (!hasColormap && (misc & 0x80) == 0)
560         {
561             // no global or local colormap
562
}
563
564             // Start reading the raster data. First we get the intial code size
565
// and compute decompressor constant values, based on this code size.
566

567         // Code size, read from GIF header
568
int codeSize = dataInput.readByte () & 0xFF;
569
570         int clearCode = (1 << codeSize); // GIF clear code
571
int EOFCode = clearCode + 1; // GIF end-of-information code
572
int firstFree = clearCode + 2; // First free code, generated per GIF spec
573
int freeCode = firstFree; // Decompressor,next free slot in hash table
574

575         // The GIF spec has it that the code size is the code size used to
576
// compute the above values is the code size given in the file, but the
577
// code size used in compression/decompression is the code size given in
578
// the file plus one. (thus the ++).
579
codeSize++;
580         int initCodeSize = codeSize; // Starting code size, used during Clear
581
int maxCode = (1 << codeSize); // limiting value for current code size
582
int readMask = maxCode - 1; // Code AND mask for current code size
583

584         // UNBLOCK:
585
// Read the raster data. Here we just transpose it from the GIF array
586
// to the raster array, turning it from a series of blocks into one long
587
// data stream, which makes life much easier for readCode ().
588
ByteArrayOutputStream bos = new ByteArrayOutputStream(10000);
589         byte [] raster = null;
590         for (int blockSize = 0; (blockSize = dataInput.readByte () & 0xFF) != 0; )
591         {
592             while (blockSize-- > 0) {
593                 bos.write(dataInput.readByte());
594             }
595         }
596         raster = bos.toByteArray();
597
598         // Allocate the 'pixels'
599
maxpixels = width * height;
600         bytePixels = new byte [maxpixels];
601         int picptr = 0;
602
603         // The hash table used by the decompressor
604
int prefix [] = new int [4096];
605         int suffix [] = new int [4096];
606         // An output array used by the decompressor
607
int outCode [] = new int [4097];
608         int outCount = 0; // Decompressor output 'stack count'
609

610         int currentCode; // Decompressor variables
611
int oldCode = 0;
612         int inCode;
613         int finChar = 0;
614         // Decompress the file, continuing until you see the GIF EOF code.
615
// One obvious enhancement is to add checking for corrupt files here.
616
int code = readCode (dataInput, raster, codeSize, readMask);
617         while (code != EOFCode)
618         {
619             // Clear code sets everything back to its initial value, then reads the
620
// immediately subsequent code as uncompressed data.
621
if (code == clearCode)
622             {
623                 codeSize = initCodeSize;
624                 maxCode = (1 << codeSize);
625                 readMask = maxCode - 1;
626                 freeCode = firstFree;
627                 code = readCode (dataInput, raster, codeSize, readMask);
628                 currentCode = oldCode = code;
629                 finChar = currentCode & bitMask;
630                 if (!interlace)
631                     bytePixels [picptr++] = (byte)finChar;
632                 else
633                     doInterlace (finChar);
634                 npixels++;
635             }
636             else
637             {
638                 // If not a clear code, must be data: save same as currentCode and inCode
639

640                 // if we're at maxcode and didn't get a clear, stop loading
641
if (freeCode >= 4096)
642                     break;
643
644                 currentCode = inCode = code;
645
646                 // If greater or equal to freeCode, not in the hash table yet;
647
// repeat the last character decoded
648
if (currentCode >= freeCode)
649                 {
650                     currentCode = oldCode;
651                     if (outCount > 4096)
652                         break;
653                     outCode [outCount++] = finChar;
654                 }
655
656                 // Unless this code is raw data, pursue the chain pointed to by currentCode
657
// through the hash table to its end; each code in the chain puts its
658
// associated output code on the output queue.
659
while (currentCode > bitMask)
660                 {
661                     if (outCount > 4096)
662                         break; // corrupt file
663
outCode [outCount++] = suffix [currentCode];
664                     currentCode = prefix[currentCode];
665                 }
666
667                 if (outCount > 4096)
668                     break;
669
670                 // The last code in the chain is treated as raw data.
671
finChar = currentCode & bitMask;
672                 outCode [outCount++] = finChar;
673
674                 // Now we put the data out to the Output routine.
675
// It's been stacked LIFO, so deal with it that way...
676

677                 // safety thing: prevent exceeding range of 'bytePixels'
678
if (npixels + outCount > maxpixels)
679                     outCount = maxpixels - npixels;
680
681                 npixels += outCount;
682                 if (!interlace)
683                     for (int i = outCount - 1; i >= 0; i--)
684                         bytePixels [picptr++] = (byte)outCode [i];
685                 else
686                     for (int i = outCount - 1; i >= 0; i--)
687                         doInterlace (outCode [i]);
688                 outCount = 0;
689
690                 // Build the hash table on-the-fly. No table is stored in the file.
691
prefix [freeCode] = oldCode;
692                 suffix [freeCode] = finChar;
693                 oldCode = inCode;
694
695                 // Point to the next slot in the table. If we exceed the current
696
// maxCode value, increment the code size unless it's already 12. If it
697
// is, do nothing: the next code decompressed better be CLEAR
698

699                 freeCode++;
700                 if (freeCode >= maxCode)
701                 {
702                     if (codeSize < 12)
703                     {
704                         codeSize++;
705                         maxCode *= 2;
706                         readMask = (1 << codeSize) - 1;
707                     }
708                 }
709             }
710
711             code = readCode (dataInput, raster, codeSize, readMask);
712             if (npixels >= maxpixels)
713                 break;
714         }
715
716         if (npixels != maxpixels)
717         {
718             if (!interlace) // clear.EOBuffer
719
for (int i = 0; i < maxpixels - npixels; i++)
720                     bytePixels [npixels + i] = 0;
721         }
722
723         /* This was part of the original GIFReader, used to make an RGB Image
724         // But we don't need the following since we only compute the bytePixels Array
725         // fill in the GifImage structure
726
727         intPixels = new int [bytePixels.length];
728         for (int i = 0; i < bytePixels.length; i++)
729             intPixels [i] = transparentIndex > 0
730                     && ((bytePixels [i] & 0xFF) == transparentIndex)
731                     ? 0
732                     : 0xFF000000
733                       | ((r [bytePixels [i]] & 0xFF) << 16)
734                       | ((g [bytePixels [i]] & 0xFF) << 8)
735                       | (b [bytePixels [i]] & 0xFF);
736         */

737
738         // We don't need any info or comments, really, so let's get rid of this
739
/*
740         fullInfo = "GIF" + ((gif89) ? "89" : "87")
741                            + ", " + bitsPerPixel + " bit" + ((bitsPerPixel == 1) ? "" : "s") + "per pixel, "
742                            + (interlace ? "" : "non-") + "interlaced.";
743
744         shortInfo = width + "x" + height + " GIF" + ((gif89) ? "89" : "87");
745         */

746
747         // comment gets handled in main LoadGIF() block-reader
748
}
749
750   /**
751    * Fetch the next code from the raster data stream. The codes can be
752    * any length from 3 to 12 bits, packed into 8-bit bytes, so we have to
753    * maintain our location in the raster array as a BIT Offset. We compute
754    * the byte Offset into the raster array by dividing this by 8, pick up
755    * three bytes, compute the bit Offset into our 24-bit chunk, shift to
756    * bring the desired code to the bottom, then mask it off and return it.
757    */

758     private int readCode (DataInputStream input, byte raster [], int codeSize, int readMask) throws IOException
759     {
760         int byteOffset = bitOffset / 8;
761         int inWordOffset = bitOffset % 8;
762         // Alan Dix modification to fix raster over-run errors
763
// int rawCode = (raster [byteOffset] & 0xFF)
764
// + ((raster [byteOffset + 1] & 0xFF) << 8);
765
int rawCode = (raster [byteOffset] & 0xFF);
766         if (byteOffset + 1 < raster.length)
767             rawCode += ((raster [byteOffset + 1] & 0xFF) << 8);
768         else if (codeSize + inWordOffset > 8)
769             warning (input, "short raster ? raster.length = " + raster.length
770                             + ", codeSize = " + codeSize
771                             + ", readMask = " + readMask);
772         // end of modification
773

774         if ( codeSize >= 8 && byteOffset + 2 < raster.length)
775             rawCode += (raster [byteOffset + 2] & 0xFF) << 16;
776         rawCode >>= (bitOffset % 8);
777         bitOffset += codeSize;
778         return rawCode & readMask;
779     }
780
781     private void doInterlace (int index)
782     {
783         if (oldYC != YC)
784         {
785             ptr = YC * width;
786             oldYC = YC;
787         }
788
789         if (YC < height)
790             bytePixels [ptr++] = (byte)index;
791
792         // Update the X-coordinate, and if it overflows, update the Y-coordinate
793
if (++XC == width)
794         {
795             // deal with the interlace as described in the GIF
796
// spec. Put the decoded scan line out to the screen if we haven't gone
797
// past the bottom of it
798
XC = 0;
799
800             switch (pass)
801             {
802                 case 0:
803                     YC += 8;
804                     if (YC >= height)
805                     {
806                         pass++;
807                         YC = 4;
808                     }
809                     break;
810
811                 case 1:
812                     YC += 8;
813                     if (YC >= height)
814                     {
815                         pass++;
816                         YC = 2;
817                     }
818                     break;
819
820                 case 2:
821                     YC += 4;
822                     if (YC >= height)
823                     {
824                         pass++;
825                         YC = 1;
826                     }
827                     break;
828
829                 case 3:
830                     YC += 2;
831                     break;
832
833                 default:
834                     break;
835             }
836         }
837     }
838
839     private void warning (InputStream input, String JavaDoc st) throws IOException
840     {
841         throw new IOException ("Warning ! " + input + " : " + st);
842     }
843 }
844
Popular Tags