home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / misc / volume9 / pbmplus / part12 / ppm / giftoppm.c < prev    next >
C/C++ Source or Header  |  1989-11-26  |  15KB  |  535 lines

  1. /*-
  2.  * giftoppm.c - Converts from a Compuserve GIF (tm) image to a PPM file.
  3.  *
  4.  * Copyright (c) 1988, 1989 by Patrick J. Naughton
  5.  *
  6.  * Author: Patrick J. Naughton
  7.  * naughton@wind.sun.com
  8.  *
  9.  * Permission to use, copy, modify, and distribute this software and its
  10.  * documentation for any purpose and without fee is hereby granted,
  11.  * provided that the above copyright notice appear in all copies and that
  12.  * both that copyright notice and this permission notice appear in
  13.  * supporting documentation.
  14.  *
  15.  * This file is provided AS IS with no warranties of any kind.  The author
  16.  * shall have no liability with respect to the infringement of copyrights,
  17.  * trade secrets or any patents by this file or any part thereof.  In no
  18.  * event will the author be liable for any lost revenue or profits or
  19.  * other special, indirect and consequential damages.
  20.  *
  21.  * Comments and additions should be sent to the author:
  22.  *
  23.  *                     Patrick J. Naughton
  24.  *                     Sun Microsystems
  25.  *                     2550 Garcia Ave, MS 14-40
  26.  *                     Mountain View, CA 94043
  27.  *                     (415) 336-1080
  28.  *
  29.  * Revision History:
  30.  * 15-Apr-89: (JP) Changed to use pbm_ error routines.
  31.  * 23-Feb-89: (JP) Changed from PBM to PPM.
  32.  * 03-Feb-89: (JP) Changed u_char to unsigned char, and unincluded types.h.
  33.  * 01-Jan-89: Added error checking and removed NEXTSHORT.
  34.  * 07-Sep-88: Added BytesPerScanline fix.
  35.  * 30-Aug-88: Allow stdin/stdout. Restructured argument parser.
  36.  * 28-Aug-88: (JP) Modified to output PBM instead of Sun raster.
  37.  * 27-Jul-88: Updated to use libpixrect to fix 386i byteswapping problems.
  38.  * 11-Apr-88: Converted to C and changed to write Sun rasterfiles.
  39.  * 19-Jan-88: GIFSLOW.PAS posted to comp.graphics by Jim Briebel,
  40.  *            a Turbo Pascal 4.0 program to painfully slowly display
  41.  *            GIF images on an EGA equipped IBM-PC.
  42.  *
  43.  * Description:
  44.  *   This program takes a Compuserve "Graphics Interchange Format" or "GIF"
  45.  * file as input and writes a PPM file.
  46.  *
  47.  * Portability:
  48.  *   To make this program convert to some image format other than PPM
  49.  * format simply seach for the tag "PPMS:" in the source and
  50.  * replace these simple mechanisms with the appropriate ones for the
  51.  * other output format.  I have marked all (six) PPM Specific pieces
  52.  * of code with this comment.
  53.  *
  54.  * SS: compile with "cc -o giftoppm -O giftoppm.c -lpixrect"
  55.  * PPMS: compile with "cc -o giftoppm -O giftoppm.c libppm.a
  56.  */
  57.  
  58. #include <stdio.h>
  59. #ifdef notdefSS
  60. #include <pixrect/pixrect_hs.h> /* SS: main Pixrect header file */
  61. #endif /*notdefSS*/
  62. #include "ppm.h"        /* PPMS: main PPM header file */
  63. #ifdef SYSV
  64. #include <string.h>
  65. #else /*SYSV*/
  66. #include <strings.h>
  67. #endif /*SYSV*/
  68.  
  69. typedef int boolean;
  70. #define True (1)
  71. #define False (0)
  72.  
  73. #define NEXTBYTE (*ptr++)
  74. #define IMAGESEP 0x2c
  75. #define INTERLACEMASK 0x40
  76. #define COLORMAPMASK 0x80
  77.  
  78. FILE *fp;
  79.  
  80. int BitOffset = 0,        /* Bit Offset of next code */
  81.     XC = 0, YC = 0,        /* Output X and Y coords of current pixel */
  82.     Pass = 0,            /* Used by output routine if interlaced pic */
  83.     OutCount = 0,        /* Decompressor output 'stack count' */
  84.     RWidth, RHeight,        /* screen dimensions */
  85.     Width, Height,        /* image dimensions */
  86.     LeftOfs, TopOfs,        /* image offset */
  87.     BitsPerPixel,        /* Bits per pixel, read from GIF header */
  88. #ifdef notdefSS
  89.     BytesPerScanline,        /* bytes per scanline in output raster */
  90. #endif /*notdefSS*/
  91.     ColorMapSize,        /* number of colors */
  92.     CodeSize,            /* Code size, read from GIF header */
  93.     InitCodeSize,        /* Starting code size, used during Clear */
  94.     Code,            /* Value returned by ReadCode */
  95.     MaxCode,            /* limiting value for current code size */
  96.     ClearCode,            /* GIF clear code */
  97.     EOFCode,            /* GIF end-of-information code */
  98.     CurCode, OldCode, InCode,    /* Decompressor variables */
  99.     FirstFree,            /* First free code, generated per GIF spec */
  100.     FreeCode,            /* Decompressor, next free slot in hash table */
  101.     FinChar,            /* Decompressor variable */
  102.     BitMask,            /* AND mask for data size */
  103.     ReadMask;            /* Code AND mask for current code size */
  104.  
  105. boolean Interlace, HasColormap;
  106. boolean Verbose = False;
  107.  
  108. #ifdef notdefSS
  109. /* SS: defined in pixrect/pixrect_hs.h */
  110. Pixrect *Output;        /* The Sun Pixrect */
  111. colormap_t Colormap;        /* The Pixrect Colormap */
  112. unsigned char *Image;            /* The result array */
  113. #endif /*notdefSS*/
  114. /* PPMS: defined in ppm.h */
  115. pixel **pixels;            /* The PPM pixel array */
  116.  
  117. unsigned char *RawGIF;            /* The heap array to hold it, raw */
  118. unsigned char *Raster;            /* The raster data stream, unblocked */
  119.  
  120.     /* The hash table used by the decompressor */
  121. int Prefix[4096];
  122. int Suffix[4096];
  123.  
  124.     /* An output array used by the decompressor */
  125. int OutCode[1025];
  126.  
  127.     /* The color map, read from the GIF header */
  128. unsigned char Red[256], Green[256], Blue[256];
  129.  
  130. char *id = "GIF87a";
  131.  
  132. main(argc, argv)
  133. int argc;
  134. char *argv[];
  135. {
  136. char *inf = NULL;
  137. char *outf = NULL;
  138. int filesize;
  139. register unsigned char ch, ch1;
  140. register unsigned char *ptr, *ptr1;
  141. register int i;
  142. char *usage = "[-vq] [-|GIFfile] [ppmfile]";
  143.  
  144.     pm_progname = argv[0];
  145.  
  146.     setbuf(stderr, NULL);
  147.  
  148.     while (--argc)
  149.     if ((++argv)[0][0] == '-')
  150.         switch (argv[0][1]) {
  151.         case 'v':
  152.         Verbose = True;
  153.         break;
  154.         case 'q':
  155.         pm_usage(usage);
  156.         case '\0':
  157.         if (inf == NULL)
  158.             inf = "Standard Input";
  159.         else if (outf == NULL)
  160.             outf = "Standard Output";
  161.         else
  162.             pm_usage(usage);
  163.         break;
  164.         default:
  165.         pm_usage(usage);
  166.         }
  167.     else if (inf == NULL)
  168.         inf = argv[0];
  169.     else if (outf == NULL)
  170.         outf = argv[0];
  171.     else
  172.         pm_usage(usage);
  173.  
  174.     if (inf == NULL || strcmp(inf, "Standard Input") == 0 || strcmp(inf, "-") == 0) {
  175.     inf = "Standard Input";
  176.     fp = stdin;
  177.     } else if (!(fp = fopen(inf, "r")))
  178.     pm_error( "%s not found", inf, 0,0,0,0 );
  179.  
  180.     /* find the size of the file */
  181.     fseek(fp, 0L, 2);
  182.     filesize = ftell(fp);
  183.     fseek(fp, 0L, 0);
  184.  
  185.     if (!(ptr = RawGIF = (unsigned char *) malloc(filesize)))
  186.     pm_error( "not enough memory to read gif file", 0,0,0,0,0 );
  187.  
  188.     if (!(Raster = (unsigned char *) malloc(filesize)))
  189.     pm_error( "not enough memory to read gif file", 0,0,0,0,0 );
  190.  
  191.     if (fread(ptr, filesize, 1, fp) != 1)
  192.     pm_error( "GIF data read failed", 0,0,0,0,0 );
  193.  
  194.     if (strncmp(ptr, id, 6))
  195.     pm_error( "%s is not a GIF file", inf, 0,0,0,0 );
  196.     ptr += 6;
  197.  
  198. /* Get variables from the GIF screen descriptor */
  199.  
  200.     ch = NEXTBYTE;
  201.     RWidth = ch + 0x100 * NEXTBYTE;    /* screen dimensions... not used. */
  202.     ch = NEXTBYTE;
  203.     RHeight = ch + 0x100 * NEXTBYTE;
  204.  
  205.     if (Verbose)
  206.     fprintf(stderr, "screen dims: %dx%d.\n", RWidth, RHeight);
  207.  
  208.     ch = NEXTBYTE;
  209.     HasColormap = ((ch & COLORMAPMASK) ? True : False);
  210.  
  211.     BitsPerPixel = (ch & 7) + 1;
  212.     ColorMapSize = 1 << BitsPerPixel;
  213.     BitMask = ColorMapSize - 1;
  214.  
  215.     ch = NEXTBYTE;        /* background color... not used. */
  216.  
  217.     if (NEXTBYTE)        /* supposed to be NULL */
  218.     pm_error( "%s is a corrupt GIF file (nonull)", inf, 0,0,0,0 );
  219.  
  220. /* Read in global colormap. */
  221.  
  222.     if (HasColormap) {
  223.     if (Verbose)
  224.         fprintf(stderr, "%s is %d bits per pixel, (%d colors).\n",
  225.         inf, BitsPerPixel, ColorMapSize);
  226.     for (i = 0; i < ColorMapSize; i++) {
  227.         Red[i] = NEXTBYTE;
  228.         Green[i] = NEXTBYTE;
  229.         Blue[i] = NEXTBYTE;
  230.     }
  231.  
  232. #ifdef notdefSS
  233. /* SS: Fill in the Pixrect colormap struct */
  234.     Colormap.type = RMT_EQUAL_RGB;
  235.     Colormap.length = ColorMapSize;
  236.     Colormap.map[0] = Red;
  237.     Colormap.map[1] = Green;
  238.     Colormap.map[2] = Blue;
  239. #endif /*notdefSS*/
  240.     /* PPMS: Don't have to do anything special here. */
  241.     }
  242.     else {
  243.         pm_message(
  244.         "%s does not have a colormap - making one up", inf, 0,0,0,0 );
  245.     Red[0] = Green[0] = Blue[0] = 0;
  246.     Red[1] = Green[1] = Blue[1] = 255;
  247.     }
  248.   
  249.   
  250.   /* Check for image seperator */
  251.  
  252.  
  253. /* Check for image seperator */
  254.  
  255.     if (NEXTBYTE != IMAGESEP)
  256.     pm_error( "%s is a corrupt GIF file (nosep)", inf, 0,0,0,0 );
  257.  
  258. /* Now read in values from the image descriptor */
  259.  
  260.     ch = NEXTBYTE;
  261.     LeftOfs = ch + 0x100 * NEXTBYTE;
  262.     ch = NEXTBYTE;
  263.     TopOfs = ch + 0x100 * NEXTBYTE;
  264.     ch = NEXTBYTE;
  265.     Width = ch + 0x100 * NEXTBYTE;
  266.     ch = NEXTBYTE;
  267.     Height = ch + 0x100 * NEXTBYTE;
  268.     Interlace = ((NEXTBYTE & INTERLACEMASK) ? True : False);
  269.  
  270.     if (Verbose)
  271.     fprintf(stderr, "Reading a %d by %d %sinterlaced image...",
  272.         Width, Height, (Interlace) ? "" : "non-");
  273.     
  274.  
  275. /* Note that I ignore the possible existence of a local color map.
  276.  * I'm told there aren't many files around that use them, and the spec
  277.  * says it's defined for future use.  This could lead to an error
  278.  * reading some files. 
  279.  */
  280.  
  281. /* Start reading the raster data. First we get the intial code size
  282.  * and compute decompressor constant values, based on this code size.
  283.  */
  284.  
  285.     CodeSize = NEXTBYTE;
  286.     ClearCode = (1 << CodeSize);
  287.     EOFCode = ClearCode + 1;
  288.     FreeCode = FirstFree = ClearCode + 2;
  289.  
  290. /* The GIF spec has it that the code size is the code size used to
  291.  * compute the above values is the code size given in the file, but the
  292.  * code size used in compression/decompression is the code size given in
  293.  * the file plus one. (thus the ++).
  294.  */
  295.  
  296.     CodeSize++;
  297.     InitCodeSize = CodeSize;
  298.     MaxCode = (1 << CodeSize);
  299.     ReadMask = MaxCode - 1;
  300.  
  301. /* Read the raster data.  Here we just transpose it from the GIF array
  302.  * to the Raster array, turning it from a series of blocks into one long
  303.  * data stream, which makes life much easier for ReadCode().
  304.  */
  305.  
  306.     ptr1 = Raster;
  307.     do {
  308.     ch = ch1 = NEXTBYTE;
  309.     while (ch--) *ptr1++ = NEXTBYTE;
  310.     if ((ptr1 - Raster) > filesize)
  311.         pm_error( "%s is a corrupt GIF file (unblock)", inf, 0,0,0,0 );
  312.     } while(ch1);
  313.  
  314.     free(RawGIF);        /* We're done with the raw data now... */
  315.  
  316.     if (Verbose) {
  317.     fprintf(stderr, "done.\n");
  318.     fprintf(stderr, "Decompressing...");
  319.     }
  320.  
  321.  
  322. #ifdef notdefSS
  323. /* SS: Allocate the Sun Pixrect and make "Image" point to the image data. */
  324.     Output = mem_create(Width, Height, 8);
  325.     if (Output == (Pixrect *) NULL)
  326.     pm_error( "not enough memory for output data", 0,0,0,0,0 );
  327.     Image = (unsigned char *) mpr_d(Output)->md_image;
  328.     BytesPerScanline = mpr_d(Output)->md_linebytes;
  329. #endif /*notdefSS*/
  330. /* PPMS: Allocate the PPM pixel array. */
  331.     pixels = ppm_allocarray(Width, Height);
  332.  
  333.  
  334. /* Decompress the file, continuing until you see the GIF EOF code.
  335.  * One obvious enhancement is to add checking for corrupt files here.
  336.  */
  337.  
  338.     Code = ReadCode();
  339.     while (Code != EOFCode) {
  340.  
  341. /* Clear code sets everything back to its initial value, then reads the
  342.  * immediately subsequent code as uncompressed data.
  343.  */
  344.  
  345.     if (Code == ClearCode) {
  346.         CodeSize = InitCodeSize;
  347.         MaxCode = (1 << CodeSize);
  348.         ReadMask = MaxCode - 1;
  349.         FreeCode = FirstFree;
  350.         CurCode = OldCode = Code = ReadCode();
  351.         FinChar = CurCode & BitMask;
  352.         AddToPixel(FinChar);
  353.     }
  354.     else {
  355.  
  356. /* If not a clear code, then must be data: save same as CurCode and InCode */
  357.  
  358.         CurCode = InCode = Code;
  359.  
  360. /* If greater or equal to FreeCode, not in the hash table yet;
  361.  * repeat the last character decoded
  362.  */
  363.  
  364.         if (CurCode >= FreeCode) {
  365.         CurCode = OldCode;
  366.         OutCode[OutCount++] = FinChar;
  367.         }
  368.  
  369. /* Unless this code is raw data, pursue the chain pointed to by CurCode
  370.  * through the hash table to its end; each code in the chain puts its
  371.  * associated output code on the output queue.
  372.  */
  373.  
  374.         while (CurCode > BitMask) {
  375.         if (OutCount > 1024)
  376.             pm_error(
  377.             "%s is a corrupt GIF file (OutCount)", inf, 0,0,0,0 );
  378.         OutCode[OutCount++] = Suffix[CurCode];
  379.         CurCode = Prefix[CurCode];
  380.         }
  381.  
  382. /* The last code in the chain is treated as raw data. */
  383.  
  384.         FinChar = CurCode & BitMask;
  385.         OutCode[OutCount++] = FinChar;
  386.  
  387. /* Now we put the data out to the Output routine.
  388.  * It's been stacked LIFO, so deal with it that way...
  389.  */
  390.  
  391.         for (i = OutCount - 1; i >= 0; i--)
  392.         AddToPixel(OutCode[i]);
  393.         OutCount = 0;
  394.  
  395. /* Build the hash table on-the-fly. No table is stored in the file. */
  396.  
  397.         Prefix[FreeCode] = OldCode;
  398.         Suffix[FreeCode] = FinChar;
  399.         OldCode = InCode;
  400.  
  401. /* Point to the next slot in the table.  If we exceed the current
  402.  * MaxCode value, increment the code size unless it's already 12.  If it
  403.  * is, do nothing: the next code decompressed better be CLEAR
  404.  */
  405.  
  406.         FreeCode++;
  407.         if (FreeCode >= MaxCode) {
  408.         if (CodeSize < 12) {
  409.             CodeSize++;
  410.             MaxCode *= 2;
  411.             ReadMask = (1 << CodeSize) - 1;
  412.         }
  413.         }
  414.     }
  415.     Code = ReadCode();
  416.     }
  417.  
  418.     free(Raster);
  419.  
  420.     if (Verbose)
  421.     fprintf(stderr, "done.\n");
  422.  
  423.     if (fp != stdin)
  424.     fclose(fp);
  425.  
  426.     if (outf == NULL || strcmp(outf, "Standard Output") == 0) {
  427.     outf = "Standard Output";
  428.     fp = stdout;
  429.     }
  430.     else {
  431.     if (!(fp = fopen(outf, "w")))
  432.         pm_error( "%s couldn't be opened for writing", outf, 0,0,0,0 );
  433.     }
  434.  
  435.     if (Verbose)
  436.     fprintf(stderr, "Writing rasterfile in %s...", outf);
  437.  
  438. #ifdef notdefSS
  439. /* SS: Pixrect Rasterfile output code. */
  440.     if (pr_dump(Output, fp, &Colormap, RT_STANDARD, 0) == PIX_ERR)
  441.     pm_error( "error writing Sun Rasterfile: %s", outf, 0,0,0,0 );
  442. #endif /*notdefSS*/
  443. /* PPMS: PPM output code. */
  444.     ppm_writeppm(stdout, pixels, Width, Height, (pixval) 255);
  445.  
  446.     if (Verbose)
  447.     fprintf(stderr, "done.\n");
  448.  
  449. #ifdef notdefSS
  450.     pr_destroy(Output);
  451. #endif /*notdefSS*/
  452.  
  453.     if (fp != stdout)
  454.     fclose(fp);
  455.  
  456.     exit(0);
  457. }
  458.  
  459.  
  460. /* Fetch the next code from the raster data stream.  The codes can be
  461.  * any length from 3 to 12 bits, packed into 8-bit bytes, so we have to
  462.  * maintain our location in the Raster array as a BIT Offset.  We compute
  463.  * the byte Offset into the raster array by dividing this by 8, pick up
  464.  * three bytes, compute the bit Offset into our 24-bit chunk, shift to
  465.  * bring the desired code to the bottom, then mask it off and return it. 
  466.  */
  467. ReadCode()
  468. {
  469. int RawCode, ByteOffset;
  470.  
  471.     ByteOffset = BitOffset / 8;
  472.     RawCode = Raster[ByteOffset] + (0x100 * Raster[ByteOffset + 1]);
  473.     if (CodeSize >= 8)
  474.     RawCode += (0x10000 * Raster[ByteOffset + 2]);
  475.     RawCode >>= (BitOffset % 8);
  476.     BitOffset += CodeSize;
  477.     return(RawCode & ReadMask);
  478. }
  479.  
  480.  
  481. AddToPixel(Index)
  482. unsigned char Index;
  483. {
  484. #ifdef notdefSS
  485.     *(Image + YC * BytesPerScanline + XC) = Index;
  486. #endif /*notdefSS*/
  487. /* PPMS: Store a pixel. */
  488.     if (YC < Height)
  489.     PPM_ASSIGN(pixels[YC][XC], Red[Index], Green[Index], Blue[Index]);
  490.  
  491. /* Update the X-coordinate, and if it overflows, update the Y-coordinate */
  492.  
  493.     if (++XC == Width) {
  494.  
  495. /* If a non-interlaced picture, just increment YC to the next scan line. 
  496.  * If it's interlaced, deal with the interlace as described in the GIF
  497.  * spec.  Put the decoded scan line out to the screen if we haven't gone
  498.  * past the bottom of it
  499.  */
  500.  
  501.     XC = 0;
  502.     if (!Interlace) YC++;
  503.     else {
  504.         switch (Pass) {
  505.         case 0:
  506.             YC += 8;
  507.             if (YC >= Height) {
  508.             Pass++;
  509.             YC = 4;
  510.             }
  511.         break;
  512.         case 1:
  513.             YC += 8;
  514.             if (YC >= Height) {
  515.             Pass++;
  516.             YC = 2;
  517.             }
  518.         break;
  519.         case 2:
  520.             YC += 4;
  521.             if (YC >= Height) {
  522.             Pass++;
  523.             YC = 1;
  524.             }
  525.         break;
  526.         case 3:
  527.             YC += 2;
  528.         break;
  529.         default:
  530.         pm_error( "can't happen", 0,0,0,0,0 );
  531.         }
  532.     }
  533.     }
  534. }
  535.