OpenCores
URL https://opencores.org/ocsvn/openrisc/openrisc/trunk

Subversion Repositories openrisc

[/] [openrisc/] [trunk/] [gnu-dev/] [or1k-gcc/] [libjava/] [classpath/] [gnu/] [javax/] [imageio/] [gif/] [GIFFile.java] - Blame information for rev 769

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 769 jeremybenn
/* GIFFile.java -- GIF decoder
2
   Copyright (C) 2006  Free Software Foundation, Inc.
3
 
4
This file is part of GNU Classpath.
5
 
6
GNU Classpath is free software; you can redistribute it and/or modify
7
it under the terms of the GNU General Public License as published by
8
the Free Software Foundation; either version 2, or (at your option)
9
any later version.
10
 
11
GNU Classpath is distributed in the hope that it will be useful, but
12
WITHOUT ANY WARRANTY; without even the implied warranty of
13
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
General Public License for more details.
15
 
16
You should have received a copy of the GNU General Public License
17
along with GNU Classpath; see the file COPYING.  If not, write to the
18
Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19
02110-1301 USA.
20
 
21
Linking this library statically or dynamically with other modules is
22
making a combined work based on this library.  Thus, the terms and
23
conditions of the GNU General Public License cover the whole
24
combination.
25
 
26
As a special exception, the copyright holders of this library give you
27
permission to link this library with independent modules to produce an
28
executable, regardless of the license terms of these independent
29
modules, and to copy and distribute the resulting executable under
30
terms of your choice, provided that you also meet, for each linked
31
independent module, the terms and conditions of the license of that
32
module.  An independent module is a module which is not derived from
33
or based on this library.  If you modify this library, you may extend
34
this exception to your version of the library, but you are not
35
obligated to do so.  If you do not wish to do so, delete this
36
exception statement from your version. */
37
 
38
package gnu.javax.imageio.gif;
39
 
40
import java.io.IOException;
41
import java.io.InputStream;
42
import java.util.Vector;
43
 
44
/**
45
 * GIFFile - reads a GIF file.
46
 *
47
 * This class only does the bare minimum work, and returns the data in raw
48
 * formats (described below). The class is J2ME compatible, and hopefully
49
 * we can keep it that way without any significant overhead.
50
 *
51
 * @author Sven de Marothy.
52
 */
53
public class GIFFile
54
{
55
  // "NETSCAPE2.0" - identifier
56
  private final static byte[] nsBlock = new byte[]
57
  {0x4e, 0x45, 0x54, 0x53, 0x43, 0x41, 0x50, 0x45, 0x32, 0x2e, 0x30 };
58
 
59
  /**
60
   * Block identifiers
61
   */
62
  private final static int EXTENSION = 0x21;
63
  private final static int LOCAL = 0x2C;
64
  private final static int TERMINATOR = 0x3B;
65
 
66
  /**
67
   * Extension block types
68
   */
69
  private final static int EXTENSION_COMMENT = 254;
70
  private final static int EXTENSION_GCONTROL = 249;
71
  private final static int EXTENSION_APPLICATION = 255;
72
 
73
  /**
74
   * Undraw commands for animation.
75
   */
76
  private final static int UNDRAW_OVERWRITE = 1;
77
  private final static int UNDRAW_RESTORE_BACKGROUND = 2;
78
  private final static int UNDRAW_RESTORE_PREVIOUS = 3;
79
 
80
  /**
81
   * Image position and dimensions (images may be partial)
82
   */
83
  private int x, y, width, height;
84
 
85
  /**
86
   * Global dimensions
87
   */
88
  private int globalWidth, globalHeight;
89
 
90
  /**
91
   * Background color index.
92
   */
93
  private byte bgIndex;
94
 
95
  /**
96
   * Number of colors
97
   */
98
  private int nColors;
99
 
100
  /**
101
   * Global palette, if any
102
   */
103
  private byte[] globalPalette;
104
 
105
  /**
106
   * Any
107
   */
108
  private boolean hasGlobalColorMap;
109
 
110
  /**
111
   * Local palette, if any (used if available)
112
   */
113
  private byte[] localPalette;
114
 
115
  /**
116
   * Interlaced GIF or not?
117
   */
118
  private boolean interlaced;
119
 
120
  /**
121
   * Has transparency?
122
   */
123
  private boolean hasTransparency;
124
 
125
  /**
126
   * Undraw mode (animations)
127
   */
128
  private int undraw;
129
 
130
  /**
131
   * Transparent index;
132
   */
133
  private int transparentIndex;
134
 
135
  /**
136
   * The uncompressed raster
137
   */
138
  private byte[] raster;
139
 
140
  /**
141
   * The compressed data (freed after uncompressing)
142
   */
143
  private byte[] compressedData;
144
 
145
  /**
146
   * Frame delay in 100ths of a second ( centiseconds, metrically )
147
   */
148
  private int duration;
149
 
150
  /**
151
   * Indices used during decompression
152
   */
153
  private int dataBlockIndex;
154
 
155
  /**
156
   * The file comment , if a comment block exists.
157
   */
158
  private String comment;
159
 
160
  /**
161
   * Fields used by getBits()
162
   */
163
  private int remainingBits = 0;
164
  private int currentBits = 0;
165
 
166
  /**
167
   * Netscape animation extension
168
   */
169
  private boolean isLooped = false;
170
 
171
  /** Number of loops, 0 = infinite */
172
  private int loops;
173
 
174
  /**
175
   * Additional frames if it's an animated GIF.
176
   */
177
  private Vector animationFrames;
178
 
179
  /**
180
   * Loads the file from an input stream, which is not closed.
181
   * @throws IOException if an I/O error occured.
182
   * @throws GIFException if some file parsing error occured
183
   */
184
  public GIFFile(InputStream in) throws IOException, GIFException
185
  {
186
    // Validate the signature
187
    if( !readSignature( in ) )
188
      throw new GIFException("Invalid GIF signature.");
189
 
190
    {
191
      byte[] data = new byte[7];
192
      if (in.read(data) != 7)
193
        throw new IOException("Couldn't read global descriptor.");
194
 
195
      globalWidth = ((data[1] & 0xFF) << 8) | (data[0] & 0xFF);
196
      globalHeight = ((data[3] & 0xFF) << 8) | (data[2] & 0xFF);
197
      byte flags = data[4];
198
      bgIndex = data[5];
199
      nColors = (1 << (( flags & 0x07) + 1));
200
      hasGlobalColorMap = ((flags & 0x80) != 0);
201
    }
202
 
203
    if( hasGlobalColorMap )
204
      {
205
        globalPalette = new byte[ nColors * 3 ];
206
        if( in.read( globalPalette ) != nColors * 3 )
207
          throw new IOException("Couldn't read color map.");
208
      }
209
 
210
    int c = in.read();
211
    while( c == EXTENSION )
212
      {
213
        readExtension( in );
214
        c = in.read();
215
      }
216
 
217
    if( c != LOCAL )
218
      throw new GIFException("Extension blocks not followed by a local descriptor ("+c+")");
219
 
220
    loadImage( in );
221
    c = in.read();
222
 
223
    if( c == TERMINATOR ) // Not an animated GIF.
224
      return;
225
 
226
    // Load animation frames. Just quit if an error occurs instead
227
    // of throwing an exception.
228
    animationFrames = new Vector();
229
    try
230
      {
231
        while( c != TERMINATOR )
232
          {
233
            animationFrames.add( new GIFFile( this, in, c ) );
234
            c = in.read();
235
          }
236
      }
237
    catch(IOException ioe)
238
      {
239
      }
240
    catch(GIFException gife)
241
      {
242
      }
243
  }
244
 
245
  /**
246
   * Constructor for additional animation frames.
247
   */
248
  private GIFFile(GIFFile parent, InputStream in, int c)
249
    throws IOException, GIFException
250
  {
251
    // Copy global properties.
252
    globalWidth = parent.globalWidth;
253
    globalHeight = parent.globalHeight;
254
    nColors = parent.nColors;
255
    globalPalette = parent.globalPalette;
256
    hasGlobalColorMap = parent.hasGlobalColorMap;
257
    interlaced = parent.interlaced;
258
    comment = parent.comment;
259
    isLooped = parent.isLooped;
260
    loops = parent.loops;
261
 
262
    while( c == EXTENSION )
263
    {
264
      readExtension( in );
265
      c = in.read();
266
    }
267
 
268
    if( c != LOCAL )
269
      throw new GIFException("Extension blocks not followed by a local descriptor ("+c+")");
270
 
271
    loadImage( in );
272
  }
273
 
274
  /**
275
   * Reads a GIF file signature from an inputstream and checks it.
276
   *
277
   * @param in - the stream (reads 6 bytes, does not close or reset).
278
   * @return true if the signature is a valid GIF signature.
279
   * @throws IOException if the signature could not be read.
280
   */
281
  public static boolean readSignature( InputStream in ) throws IOException
282
  {
283
    byte[] data = new byte[6];
284
    if (in.read(data) != 6)
285
      throw new IOException("Couldn't read signature.");
286
 
287
    if( data[0] != 0x47 || data[1] != 0x49 || data[2] != 0x46 ||
288
        data[3] != 0x38 ) // GIF8
289
      return false;
290
 
291
    if( (data[4] != 0x39 && data[4] != 0x37) || // 7 | 9
292
        (data[5] != 0x61 && data[5] != 0x62) ) // 'a' or 'b'
293
      return false;
294
    return true;
295
  }
296
 
297
 
298
  /**
299
   * Loads the image local descriptor and then loads/decodes the image raster,
300
   * and then performs any necessary postprocessing like deinterlacing.
301
   */
302
  private void loadImage(InputStream in)
303
    throws IOException, GIFException
304
  {
305
    readLocal( in );
306
 
307
    try
308
      {
309
        decodeRaster( in );
310
      }
311
    catch(ArrayIndexOutOfBoundsException aioobe)
312
      {
313
        throw new GIFException("Error decompressing image.");
314
      }
315
 
316
    if( interlaced )  // Clean up
317
      deinterlace();
318
    packPixels();
319
  }
320
 
321
  /**
322
   * Pack the pixels if it's a 2, 4 or 16 color image.
323
   * While GIF may support any number of colors from 2-256, we won't bother
324
   * trying to pack pixels not resulting in even byte boundaries.
325
   * (AWT doesn't support that anyway, and most apps do the same.)
326
   */
327
  private void packPixels()
328
  {
329
    if( nColors != 2 && nColors != 4 && nColors != 16 )
330
      return;
331
 
332
    int nbits = 1;
333
    int ppbyte = 8;
334
    if( nColors == 4 )
335
      {
336
        nbits = 2;
337
        ppbyte = 4;
338
      }
339
    else if( nColors == 16 )
340
      {
341
        nbits = 4;
342
        ppbyte = 2;
343
      }
344
 
345
    int rem = (width & (ppbyte - 1));
346
    int w = ( rem == 0 ) ? (width / ppbyte) :
347
      ((width + ppbyte - rem) / ppbyte);
348
    byte[] nr = new byte[ w * height ];
349
    for(int j = 0; j < height; j++)
350
      {
351
        for(int i = 0; i < width - ppbyte; i += ppbyte)
352
          for(int k = 0; k < ppbyte; k++)
353
            nr[ j * w + (i / ppbyte) ] |= (byte)((raster[ width * j + i + k ]
354
                                                  << (8 - nbits * (1 + k))));
355
        for(int i = 0; i < rem; i++)
356
          nr[ j * w + w - 1 ] |= (byte)((raster[ width * j + width - rem + i ]
357
                                         << (nbits * (rem - i))));
358
      }
359
    raster = nr;
360
  }
361
 
362
  /**
363
   * Returns the (global) width
364
   */
365
  public int getWidth()
366
  {
367
    return width;
368
  }
369
 
370
  /**
371
   * Returns the image height
372
   */
373
  public int getHeight()
374
  {
375
    return height;
376
  }
377
 
378
  /**
379
   * Returns the # of colors.
380
   */
381
  public int getNColors()
382
  {
383
    return nColors;
384
  }
385
 
386
  /**
387
   * Returns whether the GIF has transparency.
388
   */
389
  public boolean hasTransparency()
390
  {
391
    return hasTransparency;
392
  }
393
 
394
  /**
395
   * Returns the index of the transparent color.
396
   */
397
  public int getTransparentIndex()
398
  {
399
    return transparentIndex;
400
  }
401
 
402
  /**
403
   * Retuns the GIF file comment, or null if none exists.
404
   */
405
  public String getComment()
406
  {
407
    return comment;
408
  }
409
 
410
  /**
411
   * Get duration of the frame for animations.
412
   */
413
  public int getDuration()
414
  {
415
    return duration;
416
  }
417
 
418
  /**
419
   * Deinterlaces the image.
420
   */
421
  private void deinterlace()
422
  {
423
    byte[] nr = new byte[ width * height ];
424
    int n = 0;
425
    for(int i = 0; i < ((height + 7) >> 3); i++)
426
      {
427
        System.arraycopy( raster, n, nr, width * i * 8, width );
428
        n += width;
429
      }
430
    for(int i = 0; i < ((height + 3) >> 3); i++)
431
      {
432
        System.arraycopy( raster, n, nr, width * ( 8 * i + 4 ), width );
433
        n += width;
434
      }
435
    for(int i = 0; i < (height >> 2); i++)
436
      {
437
        System.arraycopy( raster, n, nr, width * (4 * i + 2), width );
438
        n += width;
439
      }
440
    for(int i = 0; i < (height >> 1); i++)
441
      {
442
        System.arraycopy( raster, n, nr, width * (2 * i + 1), width );
443
        n += width;
444
      }
445
    raster = nr;
446
  }
447
 
448
  /**
449
   * Reads the local descriptor
450
   */
451
  private void readLocal(InputStream in) throws IOException
452
  {
453
    byte[] data = new byte[9];
454
    if (in.read(data) != 9)
455
      throw new IOException("Couldn't read local descriptor.");
456
    x = ((data[1] & 0xFF) << 8) | (data[0] & 0xFF);
457
    y = ((data[3] & 0xFF) << 8) | (data[2] & 0xFF);
458
    width = ((data[5] & 0xFF) << 8) | (data[4] & 0xFF);
459
    height = ((data[7] & 0xFF) << 8) | (data[6] & 0xFF);
460
    byte flags = data[8];
461
    interlaced = (( flags & 0x40 ) != 0);
462
    if( (flags & 0x80) != 0 )
463
      { // has a local color map
464
        int nLocalColors = (1 << (( flags & 0x07) + 1));
465
        if( !hasGlobalColorMap )
466
          nColors = nLocalColors;
467
        localPalette = new byte[ nLocalColors * 3 ];
468
        if( in.read( localPalette ) != nLocalColors * 3 )
469
          throw new IOException("Couldn't read color map.");
470
      }
471
  }
472
 
473
  /**
474
   * Returns the image's palette in raw format
475
   * (r0,g0,b0,r1,g1,b2..r(Ncolors-1),g(Ncolors-1),b(Ncolors-1))
476
   */
477
  public byte[] getRawPalette()
478
  {
479
    return hasGlobalColorMap ? globalPalette : localPalette;
480
  }
481
 
482
  /**
483
   * Returns the image file for animated gifs.
484
   */
485
  public GIFFile getImage( int index )
486
  {
487
    if( index == 0 )
488
      return this;
489
    if( animationFrames == null )
490
      throw new ArrayIndexOutOfBoundsException("Only one image in file");
491
    return (GIFFile)animationFrames.elementAt( index - 1 );
492
  }
493
 
494
  /**
495
   * Return the image's raw image data.
496
   * If the color depth is 1,2 or 4 bits per pixel the pixels are packed
497
   * and the scanlines padded up to the nearest byte if needed.
498
   */
499
  public byte[] getRawImage()
500
  {
501
    return raster;
502
  }
503
 
504
  /**
505
   * Return the number of images in the GIF file
506
   */
507
  public int nImages()
508
  {
509
    if( animationFrames != null )
510
      return 1 + animationFrames.size();
511
    return 1;
512
  }
513
 
514
  /**
515
   * Handles extension blocks.
516
   */
517
  private void readExtension(InputStream in) throws IOException, GIFException
518
  {
519
    int functionCode = in.read();
520
    byte[] data = readData(in);
521
    switch( functionCode )
522
      {
523
      case EXTENSION_COMMENT: // comment block
524
        comment = new String(data, "8859_1");
525
        break;
526
 
527
      case EXTENSION_GCONTROL: // Graphics control extension
528
        undraw = (data[0] & 0x1C) >> 2;
529
        // allegedly there can be bad values of this.
530
        if( undraw < 1 && undraw > 3 ) undraw = 1;
531
        hasTransparency = ((data[0] & 0x01) == 1);
532
        transparentIndex = (data[3] & 0xFF);
533
        duration = ((data[2] & 0xFF) << 8) | (data[1] & 0xFF);
534
        break;
535
 
536
        // Application extension. We only parse the Netscape animation
537
        // extension here. Which is the only one most use anyway.
538
      case EXTENSION_APPLICATION:
539
        boolean isNS = true;
540
        for(int i = 0; i < nsBlock.length; i++ )
541
          if( nsBlock[i] != data[i] )
542
            isNS = false;
543
        if( isNS )
544
          {
545
            isLooped = true;
546
            loops = ((data[12] & 0xFF) << 8) | (data[13] & 0xFF);
547
          }
548
        break;
549
 
550
      default:
551
        break;
552
      }
553
  }
554
 
555
  /**
556
   * Reads a series of data blocks and merges them into a single one.
557
   */
558
  private byte[] readData(InputStream in) throws IOException
559
  {
560
    Vector v = new Vector();
561
    int totalBytes = 0;
562
 
563
    int n = in.read();
564
    do
565
      {
566
        totalBytes += n;
567
        byte[] block = new byte[ n ];
568
        in.read(block);
569
        v.add(block);
570
        n = in.read();
571
      }
572
    while( n > 0 );
573
 
574
    n = 0;
575
    byte[] bigBuffer = new byte[ totalBytes ];
576
    for( int i = 0; i < v.size(); i++ )
577
      {
578
        byte[] block = (byte[])v.elementAt(i);
579
        System.arraycopy(block, 0, bigBuffer, n, block.length);
580
        n += block.length;
581
      }
582
    return bigBuffer;
583
  }
584
 
585
  /**
586
   * Loads a compressed image block and decompresses it.
587
   */
588
  private void decodeRaster(InputStream in) throws IOException
589
  {
590
    int initialCodeSize = in.read();
591
    compressedData = readData( in );
592
    dataBlockIndex = 0;
593
 
594
    int rasterIndex = 0; // Index into the raster
595
    int clearCode = (1 << initialCodeSize); // 256 usually
596
    int endCode = clearCode + 1; // The stop code.
597
 
598
    raster = new byte[ width * height ];
599
 
600
    int codeSize = initialCodeSize + 1;
601
    int code = getBits( codeSize ); // = clear
602
    int nextCode = endCode + 1;
603
 
604
    /*
605
     * Initialize LZW dictionary
606
     *
607
     * First index - code #
608
     * Second index:
609
     * 0 = color index
610
     * 1 = parent (-1 - no parent)
611
     * 2 = first value
612
     * 3 - depth
613
     * The latter two aren't strictly necessary but make things faster, since
614
     * copying the values forward is faster than going back and looking.
615
     */
616
    short[][] dictionary = new short[ 4096 ][ 4 ];
617
 
618
    for(short i = 0; i < nColors; i ++ )
619
      {
620
        dictionary[i][0] = i;  // color index
621
        dictionary[i][1] = -1; // parent
622
        dictionary[i][2] = i;  // first
623
        dictionary[i][3] = 1;  // depth
624
      }
625
 
626
    code = getBits( codeSize ); // get second code
627
    raster[ rasterIndex++ ] = (byte)dictionary[code][0];
628
    int old = code;
629
    code = getBits( codeSize ); // start at the third code
630
    int c;
631
 
632
    do
633
      {
634
        if( code == clearCode )
635
          {
636
            codeSize = initialCodeSize + 1;
637
            nextCode = endCode + 1;
638
            // get and output second code
639
            code = getBits( codeSize );
640
            raster[ rasterIndex++ ] = (byte)dictionary[code][0];
641
            old = code;
642
          }
643
        else
644
          {
645
            dictionary[nextCode][1] = (short)old; // parent = old
646
            dictionary[nextCode][2] = dictionary[old][2]; // first pixel
647
            dictionary[nextCode][3] = (short)(dictionary[old][3] + 1); // depth
648
 
649
            // appended pixel  = first pixel of c
650
            if( code < nextCode )
651
              {
652
                dictionary[nextCode][0] = dictionary[code][2];
653
                old = code;
654
              }
655
            else // first of old
656
              {
657
                dictionary[nextCode][0] = dictionary[old][2];
658
                old = nextCode;
659
              }
660
 
661
            c = old;
662
            // output the code c
663
            int depth = dictionary[c][3];
664
            for( int i = depth - 1; i >= 0; i-- )
665
              {
666
                raster[ rasterIndex + i ] = (byte)dictionary[c][0];
667
                c = dictionary[c][1]; // go to parent.
668
              }
669
            rasterIndex += depth;
670
            nextCode ++;
671
 
672
            if( codeSize < 12 && nextCode >= (1 << codeSize) )
673
              codeSize++;
674
          }
675
        code = getBits( codeSize );
676
      }
677
    while( code != endCode && dataBlockIndex < compressedData.length );
678
 
679
    compressedData = null; // throw away compressed data.
680
  }
681
 
682
  /**
683
   * Returns nbits number of bits (in the LSBs) from compressedData
684
   */
685
  private int getBits( int nbits )
686
  {
687
    while( nbits > remainingBits )
688
      {
689
        int c = (compressedData[ dataBlockIndex++ ] & 0xFF) << remainingBits;
690
        currentBits |= c;
691
        remainingBits += 8;
692
      }
693
    int rval = (currentBits & ((1 << nbits) - 1));
694
    currentBits = (currentBits >> nbits);
695
    remainingBits -= nbits;
696
    return rval;
697
  }
698
 
699
  /**
700
   * Generic exception used by GIFFile to report decoding errors.
701
   */
702
  public static class GIFException extends Exception
703
  {
704
    public GIFException(String message)
705
    {
706
      super(message);
707
    }
708
  }
709
}

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.