Back to index

plt-scheme  4.2.1
wx_gif.cc
Go to the documentation of this file.
00001 /*
00002  * xvgif.c  -  GIF loading code for 'xv'.  Based strongly on...
00003  *
00004  * gif2ras.c - Converts from a Compuserve GIF (tm) image to a Sun Raster image.
00005  *
00006  * Copyright (c) 1988, 1989 by Patrick J. Naughton
00007  *
00008  * Author: Patrick J. Naughton
00009  * naughton@wind.sun.com
00010  *
00011  * Permission to use, copy, modify, and distribute this software and its
00012  * documentation for any purpose and without fee is hereby granted,
00013  * provided that the above copyright notice appear in all copies and that
00014  * both that copyright notice and this permission notice appear in
00015  * supporting documentation.
00016  *
00017  * This file is provided AS IS with no warranties of any kind.  The author
00018  * shall have no liability with respect to the infringement of copyrights,
00019  * trade secrets or any patents by this file or any part thereof.  In no
00020  * event will the author be liable for any lost revenue or profits or
00021  * other special, indirect and consequential damages.
00022  *
00023  */
00024 
00025 /*
00026  * Copyright 1989, 1990 by the University of Pennsylvania
00027  *
00028  * Permission to use, copy, and distribute for non-commercial purposes,
00029  * is hereby granted without fee, providing that the above copyright
00030  * notice appear in all copies and that both the copyright notice and this
00031  * permission notice appear in supporting documentation.
00032  *
00033  * The software may be modified for your own purposes, but modified versions
00034  * may not be distributed.
00035  *
00036  * This software is provided "as is" without any express or implied warranty.
00037  */
00038 
00039 #include <stdlib.h>
00040 #include "wx_image.h"
00041 
00042 #ifdef MZ_PRECISE_GC
00043 END_XFORM_ARITH;
00044 #endif
00045 
00046 #define NEXTBYTE (*ptr++)
00047 #define IMAGESEP 0x2c
00048 #define EXTENSION 0x21
00049 #define INTERLACEMASK 0x40
00050 #define COLORMAPMASK 0x80
00051 
00052 FILE *fp;
00053 
00054 int BitOffset = 0,          /* Bit Offset of next code */
00055     XC = 0, YC = 0,         /* Output X and Y coords of current pixel */
00056     Pass = 0,               /* Used by output routine if interlaced pic */
00057     OutCount = 0,           /* Decompressor output 'stack count' */
00058     RWidth, RHeight,        /* screen dimensions */
00059     Width, Height,          /* image dimensions */
00060     LeftOfs, TopOfs,        /* image offset */
00061     BitsPerPixel,           /* Bits per pixel, read from GIF header */
00062     BytesPerScanline,              /* bytes per scanline in output raster */
00063     ColorMapSize,           /* number of colors */
00064     Background,                    /* background color */
00065     CodeSize,               /* Code size, read from GIF header */
00066     InitCodeSize,           /* Starting code size, used during Clear */
00067     Code,                   /* Value returned by ReadCode */
00068     MaxCode,                /* limiting value for current code size */
00069     ClearCode,                     /* GIF clear code */
00070     EOFCode,                /* GIF end-of-information code */
00071     CurCode, OldCode, InCode,      /* Decompressor variables */
00072     FirstFree,                     /* First free code, generated per GIF spec */
00073     FreeCode,               /* Decompressor,next free slot in hash table */
00074     FinChar,                /* Decompressor variable */
00075     BitMask,                /* AND mask for data size */
00076     ReadMask,               /* Code AND mask for current code size */
00077     Misc;                       /* miscellaneous bits (interlace, local cmap)*/
00078 
00079 
00080 Bool Interlace, HasColormap;
00081 
00082 byte *RawGIF;               /* The heap array to hold it, raw */
00083 byte *Raster;               /* The raster data stream, unblocked */
00084 
00085     /* The hash table used by the decompressor */
00086 int Prefix[4096];
00087 int Suffix[4096];
00088 
00089     /* An output array used by the decompressor */
00090 int OutCode[1025];
00091 
00092 char *id = "GIF87a";
00093 /* MATTHEW: */
00094 char *id2 = "GIF89a";
00095 
00096 static int EGApalette[16][3] = {
00097   {0,0,0},       {0,0,128},     {0,128,0},     {0,128,128}, 
00098   {128,0,0},     {128,0,128},   {128,128,0},   {200,200,200},
00099   {100,100,100}, {100,100,255}, {100,255,100}, {100,255,255},
00100   {255,100,100}, {255,100,255}, {255,255,100}, {255,255,255} };
00101 
00102 int filesize;
00103 
00104 /*****************************/
00105 int wxImage::LoadGIF(char *fname, int /* nc */)
00106 {
00107   register byte  ch, ch1;
00108   register byte *ptr, *ptr1, *picptr;
00109   register int   i;
00110   int            npixels, maxpixels;
00111 
00112   /* initialize variables */
00113   BitOffset = XC = YC = Pass = OutCount = npixels = maxpixels = 0;
00114   RawGIF = Raster = pic = NULL;
00115   
00116   fp = fopen(fname,"r");
00117   if (!fp) {
00118     fprintf(stderr,"LoadGIF() - unable to open file '%s'\n", fname);
00119     return 1;
00120   }
00121   
00122   /* find the size of the file */
00123   fseek(fp, 0L, 2);
00124   filesize = ftell(fp);
00125   fseek(fp, 0L, 0);
00126   
00127   /* the +256's are so we can read truncated GIF files without fear of 
00128      segmentation violation */
00129   ptr = (byte *) malloc(filesize+256);
00130   RawGIF = ptr;
00131   if (!ptr) {
00132     fclose(fp);
00133     return( GifError("not enough memory to read gif file") );
00134   }
00135   
00136   if (!(Raster = (byte *) malloc(filesize+256))) {
00137     fclose(fp);
00138     return( GifError("not enough memory to read gif file") );
00139   }
00140   
00141   if (fread(ptr, filesize, 1, fp) != 1) {
00142     fclose(fp);
00143     return( GifError("GIF data read failed") );
00144   }
00145   
00146   /* MATTHEW */
00147   if (strncmp((const char *)ptr, (const char *)id, 6)
00148       && strncmp((const char *)ptr, (const char *)id2, 6)) {
00149     fclose(fp);
00150     return( GifError("not a GIF file"));
00151   }
00152   
00153   ptr += 6;
00154   
00155   /* Get variables from the GIF screen descriptor */
00156   
00157   ch = NEXTBYTE;
00158   RWidth = ch + 0x100 * NEXTBYTE;  /* screen dimensions... not used. */
00159   ch = NEXTBYTE;
00160   RHeight = ch + 0x100 * NEXTBYTE;
00161   
00162   ch = NEXTBYTE;
00163   HasColormap = ((ch & COLORMAPMASK) ? TRUE : FALSE);
00164   
00165   BitsPerPixel = (ch & 7) + 1;
00166   numcols = ColorMapSize = 1 << BitsPerPixel;
00167   BitMask = ColorMapSize - 1;
00168   
00169   Background = NEXTBYTE;           /* background color... not used. */
00170   
00171   if (NEXTBYTE) { /* NULL for normal GIF */
00172     /* Non-null means animated; we pretend it's normal. */
00173     if (0) {
00174       fclose(fp);
00175       return( GifError("corrupt GIF file (screen descriptor)") );
00176     }
00177   }
00178   
00179   
00180   /* Read in global colormap. */
00181   
00182   if (HasColormap)
00183     for (i=0; i<ColorMapSize; i++) {
00184       r[i] = NEXTBYTE;
00185       g[i] = NEXTBYTE;
00186       b[i] = NEXTBYTE;
00187     }
00188   else {  /* no colormap in GIF file */
00189     /* put std EGA palette (repeated 16 times) into colormap, for lack of
00190        anything better to do */
00191 
00192     for (i=0; i<256; i++) {
00193       r[i] = EGApalette[i&15][0];
00194       g[i] = EGApalette[i&15][1];
00195       b[i] = EGApalette[i&15][2];
00196     }
00197   }
00198 
00199   while ( (i=NEXTBYTE) == EXTENSION) {  /* parse extension blocks */
00200     int i, fn, blocksize, aspnum, aspden;
00201 
00202     /* read extension block */
00203     fn = NEXTBYTE;
00204 
00205     do {
00206       i = 0;  blocksize = NEXTBYTE;
00207       while (i < blocksize) {
00208        if (fn == 'R' && blocksize == 2) {   /* aspect ratio extension */
00209          aspnum = NEXTBYTE;  i++;
00210          aspden = NEXTBYTE;  i++;
00211          if (aspden>0 && aspnum>0) 
00212            normaspect = (float) aspnum / (float) aspden;
00213          else { normaspect = 1.0;  aspnum = aspden = 1; }
00214 
00215           /* fprintf(stderr,"aspect extension: %d:%d = %f\n", 
00216                 aspnum, aspden,normaspect); */
00217        } else if (fn == 0xf9 && blocksize == 4) {   /* graphic control extension */
00218          int flags, ti;
00219          flags = NEXTBYTE;
00220          (void)NEXTBYTE;
00221          (void)NEXTBYTE;
00222          ti = NEXTBYTE;
00223          i += 4;
00224          if (flags & 0x1) {
00225            if (transparent_index == -1)
00226              transparent_index = ti;
00227          }
00228        } else { (void)NEXTBYTE;  i++; }
00229       }
00230     } while (blocksize);
00231   }
00232 
00233 
00234   /* Check for image seperator */
00235   if (i != IMAGESEP) {
00236     fclose(fp);
00237     return( GifError("corrupt GIF file (no image separator)") );
00238   }
00239   
00240   /* Now read in values from the image descriptor */
00241   
00242   ch = NEXTBYTE;
00243   LeftOfs = ch + 0x100 * NEXTBYTE;
00244   ch = NEXTBYTE;
00245   TopOfs = ch + 0x100 * NEXTBYTE;
00246   ch = NEXTBYTE;
00247   Width = ch + 0x100 * NEXTBYTE;
00248   ch = NEXTBYTE;
00249   Height = ch + 0x100 * NEXTBYTE;
00250 
00251   Misc = NEXTBYTE;
00252   Interlace = ((Misc & INTERLACEMASK) ? TRUE : FALSE);
00253 
00254   if (Misc & 0x80) {
00255     for (i=0; i< 1 << ((Misc&7)+1); i++) {
00256       r[i] = NEXTBYTE;
00257       g[i] = NEXTBYTE;
00258       b[i] = NEXTBYTE;
00259     }
00260   }
00261 
00262 
00263   if (!HasColormap && !(Misc&0x80)) {
00264     /* no global or local colormap */
00265     fprintf(stderr, "No colormap in this GIF file.  Assuming EGA colors.");
00266   }
00267     
00268   /* Start reading the raster data. First we get the intial code size
00269    * and compute decompressor constant values, based on this code size.
00270    */
00271   
00272   CodeSize = NEXTBYTE;
00273   ClearCode = (1 << CodeSize);
00274   EOFCode = ClearCode + 1;
00275   FreeCode = FirstFree = ClearCode + 2;
00276   
00277   /* The GIF spec has it that the code size is the code size used to
00278    * compute the above values is the code size given in the file, but the
00279    * code size used in compression/decompression is the code size given in
00280    * the file plus one. (thus the ++).
00281    */
00282   
00283   CodeSize++;
00284   InitCodeSize = CodeSize;
00285   MaxCode = (1 << CodeSize);
00286   ReadMask = MaxCode - 1;
00287   
00288 
00289 
00290   /* UNBLOCK:
00291    * Read the raster data.  Here we just transpose it from the GIF array
00292    * to the Raster array, turning it from a series of blocks into one long
00293    * data stream, which makes life much easier for ReadCode().
00294    */
00295   
00296   ptr1 = Raster;
00297   do {
00298     ch = ch1 = NEXTBYTE;
00299     while (ch--) { *ptr1 = NEXTBYTE; ptr1++; }
00300     if ((ptr - RawGIF) > filesize) {
00301       fprintf(stderr,
00302              "This GIF file seems to be truncated.  Winging it.\n");
00303       break;
00304     }
00305   } while(ch1);
00306   free(RawGIF);       RawGIF = NULL;      /* We're done with the raw data now */
00307 
00308 
00309 
00310   if (imgDEBUG) {
00311     fprintf(stderr,"xv: LoadGIF() - picture is %dx%d, %d bits, %sinterlaced\n",
00312            Width, Height, BitsPerPixel, Interlace ? "" : "non-");
00313   }
00314 
00315 /*  
00316   fprintf(stderr, "GIF, %d bits per pixel, %sinterlaced.  (%d bytes)",
00317          BitsPerPixel, Interlace ? "" : "non-", filesize);
00318 */
00319 
00320 
00321   /* Allocate the 'pic' */
00322   pWIDE = Width;  pHIGH = Height;
00323   maxpixels = Width*Height;
00324   picptr = (byte *) malloc(maxpixels);
00325   pic = picptr;
00326   if (!pic) {
00327     fclose(fp);
00328     return( GifError("not enough memory for 'pic'") );
00329   }
00330 
00331   
00332   /* Decompress the file, continuing until you see the GIF EOF code.
00333    * One obvious enhancement is to add checking for corrupt files here.
00334    */
00335   
00336   Code = ReadCode();
00337   while (Code != EOFCode) {
00338     /* Clear code sets everything back to its initial value, then reads the
00339      * immediately subsequent code as uncompressed data.
00340      */
00341 
00342     if (Code == ClearCode) {
00343       CodeSize = InitCodeSize;
00344       MaxCode = (1 << CodeSize);
00345       ReadMask = MaxCode - 1;
00346       FreeCode = FirstFree;
00347       Code = ReadCode();
00348       CurCode = OldCode = Code;
00349       FinChar = CurCode & BitMask;
00350       if (!Interlace) *picptr++ = FinChar;
00351          else DoInterlace(FinChar);
00352       npixels++;
00353     }
00354     else {
00355       /* If not a clear code, must be data: save same as CurCode and InCode */
00356 
00357       /* if we're at maxcode and didn't get a clear, stop loading */
00358       if (FreeCode>=4096) { /* printf("freecode blew up\n"); */
00359                          break; }
00360 
00361       CurCode = InCode = Code;
00362       
00363       /* If greater or equal to FreeCode, not in the hash table yet;
00364        * repeat the last character decoded
00365        */
00366       
00367       if (CurCode >= FreeCode) {
00368        CurCode = OldCode;
00369        if (OutCount > 1024) {  /* printf("outcount1 blew up\n"); */ break; }
00370        OutCode[OutCount++] = FinChar;
00371       }
00372       
00373       /* Unless this code is raw data, pursue the chain pointed to by CurCode
00374        * through the hash table to its end; each code in the chain puts its
00375        * associated output code on the output queue.
00376        */
00377       
00378       while (CurCode > BitMask) {
00379        if (OutCount > 1024) break;   /* corrupt file */
00380        OutCode[OutCount++] = Suffix[CurCode];
00381        CurCode = Prefix[CurCode];
00382       }
00383       
00384       if (OutCount > 1024) { /* printf("outcount blew up\n"); */ break; }
00385       
00386       /* The last code in the chain is treated as raw data. */
00387       
00388       FinChar = CurCode & BitMask;
00389       OutCode[OutCount++] = FinChar;
00390       
00391       /* Now we put the data out to the Output routine.
00392        * It's been stacked LIFO, so deal with it that way...
00393        */
00394 
00395       /* safety thing:  prevent exceeding range of 'pic' */
00396       if (npixels + OutCount > maxpixels) OutCount = maxpixels-npixels;
00397        
00398       npixels += OutCount;
00399       if (!Interlace) { 
00400        for (i=OutCount-1; i>=0; i--) { *picptr++ = OutCode[i]; }
00401       } else { 
00402        for (i=OutCount-1; i>=0; i--) { DoInterlace(OutCode[i]); } 
00403       }
00404       OutCount = 0;
00405 
00406       /* Build the hash table on-the-fly. No table is stored in the file. */
00407       
00408       Prefix[FreeCode] = OldCode;
00409       Suffix[FreeCode] = FinChar;
00410       OldCode = InCode;
00411       
00412       /* Point to the next slot in the table.  If we exceed the current
00413        * MaxCode value, increment the code size unless it's already 12.  If it
00414        * is, do nothing: the next code decompressed better be CLEAR
00415        */
00416       
00417       FreeCode++;
00418       if (FreeCode >= MaxCode) {
00419        if (CodeSize < 12) {
00420          CodeSize++;
00421          MaxCode *= 2;
00422          ReadMask = (1 << CodeSize) - 1;
00423        }
00424       }
00425     }
00426     Code = ReadCode();
00427     if (npixels >= maxpixels) break;
00428   }
00429   free(Raster);  Raster = NULL;
00430   
00431   if (npixels != maxpixels) {
00432     fprintf(stderr, "This GIF file seems to be truncated.  Winging it.\n");
00433     memset(pic+npixels, 0, maxpixels-npixels);  /* clear to EOBuffer */
00434   }
00435 
00436   if (fp != stdin) fclose(fp);
00437 
00438   return 0;
00439 }
00440 
00441 
00442 /* Fetch the next code from the raster data stream.  The codes can be
00443  * any length from 3 to 12 bits, packed into 8-bit bytes, so we have to
00444  * maintain our location in the Raster array as a BIT Offset.  We compute
00445  * the byte Offset into the raster array by dividing this by 8, pick up
00446  * three bytes, compute the bit Offset into our 24-bit chunk, shift to
00447  * bring the desired code to the bottom, then mask it off and return it. 
00448  */
00449 
00450 int wxImage::ReadCode()
00451 {
00452   int RawCode, ByteOffset;
00453   
00454   ByteOffset = BitOffset / 8;
00455   RawCode = Raster[ByteOffset] + (Raster[ByteOffset + 1] << 8);
00456   if (CodeSize >= 8)
00457     RawCode += (Raster[ByteOffset + 2] << 16);
00458   RawCode >>= (BitOffset % 8);
00459   BitOffset += CodeSize;
00460 
00461   return(RawCode & ReadMask);
00462 }
00463 
00464 
00465 /***************************/
00466 void wxImage::DoInterlace(byte Index)
00467 {
00468   static byte *ptr = NULL;
00469   static int   oldYC = -1;
00470   
00471   if (oldYC != YC) {  ptr = pic + YC * Width;  oldYC = YC; }
00472   
00473   if (YC<Height)
00474     *ptr++ = Index;
00475   
00476   /* Update the X-coordinate, and if it overflows, update the Y-coordinate */
00477   
00478   if (++XC == Width) {
00479     
00480     /* deal with the interlace as described in the GIF
00481      * spec.  Put the decoded scan line out to the screen if we haven't gone
00482      * past the bottom of it
00483      */
00484     
00485     XC = 0;
00486     
00487     switch (Pass) {
00488     case 0:
00489       YC += 8;
00490       if (YC >= Height) { Pass++; YC = 4; }
00491       break;
00492       
00493     case 1:
00494       YC += 8;
00495       if (YC >= Height) { Pass++; YC = 2; }
00496       break;
00497       
00498     case 2:
00499       YC += 4;
00500       if (YC >= Height) { Pass++; YC = 1; }
00501       break;
00502       
00503     case 3:
00504       YC += 2;  break;
00505       
00506     default:
00507       break;
00508     }
00509   }
00510 }
00511 
00512 
00513       
00514 /*****************************/
00515 int wxImage::GifError(char *st)
00516 {
00517   fprintf(stderr,"LoadGIF() - %s\n",st);
00518   
00519   if (RawGIF != NULL) free(RawGIF);
00520   if (Raster != NULL) free(Raster);
00521   if (pic    != NULL) free(pic);
00522   
00523   return -1;
00524 }
00525