home *** CD-ROM | disk | FTP | other *** search
/ Club Amiga de Montreal - CAM / CAM_CD_1.iso / files / 309.lha / PBM_PLUS / ppm / giftoppm.c < prev    next >
C/C++ Source or Header  |  1980-12-04  |  15KB  |  525 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    SYSV
  60. #include <string.h>
  61. #else    SYSV
  62. #include <strings.h>
  63. #endif    SYSV
  64. #ifdef notdefSS
  65. #include <pixrect/pixrect_hs.h> /* SS: main Pixrect header file */
  66. #endif notdefSS
  67. #include "ppm.h"        /* PPMS: main PPM header file */
  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.     BytesPerScanline,        /* bytes per scanline in output raster */
  89.     ColorMapSize,        /* number of colors */
  90.     CodeSize,            /* Code size, read from GIF header */
  91.     InitCodeSize,        /* Starting code size, used during Clear */
  92.     Code,            /* Value returned by ReadCode */
  93.     MaxCode,            /* limiting value for current code size */
  94.     ClearCode,            /* GIF clear code */
  95.     EOFCode,            /* GIF end-of-information code */
  96.     CurCode, OldCode, InCode,    /* Decompressor variables */
  97.     FirstFree,            /* First free code, generated per GIF spec */
  98.     FreeCode,            /* Decompressor, next free slot in hash table */
  99.     FinChar,            /* Decompressor variable */
  100.     BitMask,            /* AND mask for data size */
  101.     ReadMask;            /* Code AND mask for current code size */
  102.  
  103. boolean Interlace, HasColormap;
  104. boolean Verbose = False;
  105.  
  106. #ifdef notdefSS
  107. /* SS: defined in pixrect/pixrect_hs.h */
  108. Pixrect *Output;        /* The Sun Pixrect */
  109. colormap_t Colormap;        /* The Pixrect Colormap */
  110. unsigned char *Image;            /* The result array */
  111. #endif notdefSS
  112. /* PPMS: defined in ppm.h */
  113. pixel **pixels;            /* The PPM pixel array */
  114.  
  115. unsigned char *RawGIF;            /* The heap array to hold it, raw */
  116. unsigned char *Raster;            /* The raster data stream, unblocked */
  117.  
  118.     /* The hash table used by the decompressor */
  119. int Prefix[4096];
  120. int Suffix[4096];
  121.  
  122.     /* An output array used by the decompressor */
  123. int OutCode[1025];
  124.  
  125.     /* The color map, read from the GIF header */
  126. unsigned char Red[256], Green[256], Blue[256];
  127.  
  128. char *id = "GIF87a";
  129.  
  130. main(argc, argv)
  131. int argc;
  132. char *argv[];
  133. {
  134. char *inf = NULL;
  135. char *outf = NULL;
  136. int filesize;
  137. register unsigned char ch, ch1;
  138. register unsigned char *ptr, *ptr1;
  139. register int i;
  140. char *usage = "[-vq] [-|GIFfile] [ppmfile]";
  141.  
  142.     pm_progname = argv[0];
  143.  
  144.     setbuf(stderr, NULL);
  145.  
  146.     while (--argc)
  147.     if ((++argv)[0][0] == '-')
  148.         switch (argv[0][1]) {
  149.         case 'v':
  150.         Verbose = True;
  151.         break;
  152.         case 'q':
  153.         pm_usage(usage);
  154.         case '\0':
  155.         if (inf == NULL)
  156.             inf = "Standard Input";
  157.         else if (outf == NULL)
  158.             outf = "Standard Output";
  159.         else
  160.             pm_usage(usage);
  161.         break;
  162.         default:
  163.         pm_usage(usage);
  164.         }
  165.     else if (inf == NULL)
  166.         inf = argv[0];
  167.     else if (outf == NULL)
  168.         outf = argv[0];
  169.     else
  170.         pm_usage(usage);
  171.  
  172.     if (inf == NULL || strcmp(inf, "Standard Input") == 0 || strcmp(inf, "-") == 0) {
  173.     inf = "Standard Input";
  174.     fp = stdin;
  175.     } else if (!(fp = fopen(inf, "r")))
  176.     pm_error( "%s not found", inf, 0,0,0,0 );
  177.  
  178.     /* find the size of the file */
  179.     fseek(fp, 0L, 2);
  180.     filesize = ftell(fp);
  181.     fseek(fp, 0L, 0);
  182.  
  183.     if (!(ptr = RawGIF = (unsigned char *) malloc(filesize)))
  184.     pm_error( "not enough memory to read gif file", 0,0,0,0,0 );
  185.  
  186.     if (!(Raster = (unsigned char *) malloc(filesize)))
  187.     pm_error( "not enough memory to read gif file", 0,0,0,0,0 );
  188.  
  189.     if (fread(ptr, filesize, 1, fp) != 1)
  190.     pm_error( "GIF data read failed", 0,0,0,0,0 );
  191.  
  192.     if (strncmp(ptr, id, 6))
  193.     pm_error( "%s is not a GIF file", inf, 0,0,0,0 );
  194.     ptr += 6;
  195.  
  196. /* Get variables from the GIF screen descriptor */
  197.  
  198.     ch = NEXTBYTE;
  199.     RWidth = ch + 0x100 * NEXTBYTE;    /* screen dimensions... not used. */
  200.     ch = NEXTBYTE;
  201.     RHeight = ch + 0x100 * NEXTBYTE;
  202.  
  203.     if (Verbose)
  204.     fprintf(stderr, "screen dims: %dx%d.\n", RWidth, RHeight);
  205.  
  206.     ch = NEXTBYTE;
  207.     HasColormap = ((ch & COLORMAPMASK) ? True : False);
  208.  
  209.     BitsPerPixel = (ch & 7) + 1;
  210.     ColorMapSize = 1 << BitsPerPixel;
  211.     BitMask = ColorMapSize - 1;
  212.  
  213.     ch = NEXTBYTE;        /* background color... not used. */
  214.  
  215.     if (NEXTBYTE)        /* supposed to be NULL */
  216.     pm_error( "%s is a corrupt GIF file (nonull)", inf, 0,0,0,0 );
  217.  
  218. /* Read in global colormap. */
  219.  
  220.     if (HasColormap) {
  221.     if (Verbose)
  222.         fprintf(stderr, "%s is %d bits per pixel, (%d colors).\n",
  223.         inf, BitsPerPixel, ColorMapSize);
  224.     for (i = 0; i < ColorMapSize; i++) {
  225.         Red[i] = NEXTBYTE;
  226.         Green[i] = NEXTBYTE;
  227.         Blue[i] = NEXTBYTE;
  228.     }
  229.  
  230. #ifdef notdefSS
  231. /* SS: Fill in the Pixrect colormap struct */
  232.     Colormap.type = RMT_EQUAL_RGB;
  233.     Colormap.length = ColorMapSize;
  234.     Colormap.map[0] = Red;
  235.     Colormap.map[1] = Green;
  236.     Colormap.map[2] = Blue;
  237. #endif notdefSS
  238.     /* PPMS: Don't have to do anything special here. */
  239.     }
  240.     else pm_error( "%s does not have a colormap", inf, 0,0,0,0 );
  241.  
  242.  
  243. /* Check for image seperator */
  244.  
  245.     if (NEXTBYTE != IMAGESEP)
  246.     pm_error( "%s is a corrupt GIF file (nosep)", inf, 0,0,0,0 );
  247.  
  248. /* Now read in values from the image descriptor */
  249.  
  250.     ch = NEXTBYTE;
  251.     LeftOfs = ch + 0x100 * NEXTBYTE;
  252.     ch = NEXTBYTE;
  253.     TopOfs = ch + 0x100 * NEXTBYTE;
  254.     ch = NEXTBYTE;
  255.     Width = ch + 0x100 * NEXTBYTE;
  256.     ch = NEXTBYTE;
  257.     Height = ch + 0x100 * NEXTBYTE;
  258.     Interlace = ((NEXTBYTE & INTERLACEMASK) ? True : False);
  259.  
  260.     if (Verbose)
  261.     fprintf(stderr, "Reading a %d by %d %sinterlaced image...",
  262.         Width, Height, (Interlace) ? "" : "non-");
  263.     
  264.  
  265. /* Note that I ignore the possible existence of a local color map.
  266.  * I'm told there aren't many files around that use them, and the spec
  267.  * says it's defined for future use.  This could lead to an error
  268.  * reading some files. 
  269.  */
  270.  
  271. /* Start reading the raster data. First we get the intial code size
  272.  * and compute decompressor constant values, based on this code size.
  273.  */
  274.  
  275.     CodeSize = NEXTBYTE;
  276.     ClearCode = (1 << CodeSize);
  277.     EOFCode = ClearCode + 1;
  278.     FreeCode = FirstFree = ClearCode + 2;
  279.  
  280. /* The GIF spec has it that the code size is the code size used to
  281.  * compute the above values is the code size given in the file, but the
  282.  * code size used in compression/decompression is the code size given in
  283.  * the file plus one. (thus the ++).
  284.  */
  285.  
  286.     CodeSize++;
  287.     InitCodeSize = CodeSize;
  288.     MaxCode = (1 << CodeSize);
  289.     ReadMask = MaxCode - 1;
  290.  
  291. /* Read the raster data.  Here we just transpose it from the GIF array
  292.  * to the Raster array, turning it from a series of blocks into one long
  293.  * data stream, which makes life much easier for ReadCode().
  294.  */
  295.  
  296.     ptr1 = Raster;
  297.     do {
  298.     ch = ch1 = NEXTBYTE;
  299.     while (ch--) *ptr1++ = NEXTBYTE;
  300.     if ((ptr1 - Raster) > filesize)
  301.         pm_error( "%s is a corrupt GIF file (unblock)", inf, 0,0,0,0 );
  302.     } while(ch1);
  303.  
  304.     free(RawGIF);        /* We're done with the raw data now... */
  305.  
  306.     if (Verbose) {
  307.     fprintf(stderr, "done.\n");
  308.     fprintf(stderr, "Decompressing...");
  309.     }
  310.  
  311.  
  312. #ifdef notdefSS
  313. /* SS: Allocate the Sun Pixrect and make "Image" point to the image data. */
  314.     Output = mem_create(Width, Height, 8);
  315.     if (Output == (Pixrect *) NULL)
  316.     pm_error( "not enough memory for output data", 0,0,0,0,0 );
  317.     Image = (unsigned char *) mpr_d(Output)->md_image;
  318.     BytesPerScanline = mpr_d(Output)->md_linebytes;
  319. #endif notdefSS
  320. /* PPMS: Allocate the PPM pixel array. */
  321.     pixels = ppm_allocarray(Width, Height);
  322.  
  323.  
  324. /* Decompress the file, continuing until you see the GIF EOF code.
  325.  * One obvious enhancement is to add checking for corrupt files here.
  326.  */
  327.  
  328.     Code = ReadCode();
  329.     while (Code != EOFCode) {
  330.  
  331. /* Clear code sets everything back to its initial value, then reads the
  332.  * immediately subsequent code as uncompressed data.
  333.  */
  334.  
  335.     if (Code == ClearCode) {
  336.         CodeSize = InitCodeSize;
  337.         MaxCode = (1 << CodeSize);
  338.         ReadMask = MaxCode - 1;
  339.         FreeCode = FirstFree;
  340.         CurCode = OldCode = Code = ReadCode();
  341.         FinChar = CurCode & BitMask;
  342.         AddToPixel(FinChar);
  343.     }
  344.     else {
  345.  
  346. /* If not a clear code, then must be data: save same as CurCode and InCode */
  347.  
  348.         CurCode = InCode = Code;
  349.  
  350. /* If greater or equal to FreeCode, not in the hash table yet;
  351.  * repeat the last character decoded
  352.  */
  353.  
  354.         if (CurCode >= FreeCode) {
  355.         CurCode = OldCode;
  356.         OutCode[OutCount++] = FinChar;
  357.         }
  358.  
  359. /* Unless this code is raw data, pursue the chain pointed to by CurCode
  360.  * through the hash table to its end; each code in the chain puts its
  361.  * associated output code on the output queue.
  362.  */
  363.  
  364.         while (CurCode > BitMask) {
  365.         if (OutCount > 1024)
  366.             pm_error(
  367.             "%s is a corrupt GIF file (OutCount)", inf, 0,0,0,0 );
  368.         OutCode[OutCount++] = Suffix[CurCode];
  369.         CurCode = Prefix[CurCode];
  370.         }
  371.  
  372. /* The last code in the chain is treated as raw data. */
  373.  
  374.         FinChar = CurCode & BitMask;
  375.         OutCode[OutCount++] = FinChar;
  376.  
  377. /* Now we put the data out to the Output routine.
  378.  * It's been stacked LIFO, so deal with it that way...
  379.  */
  380.  
  381.         for (i = OutCount - 1; i >= 0; i--)
  382.         AddToPixel(OutCode[i]);
  383.         OutCount = 0;
  384.  
  385. /* Build the hash table on-the-fly. No table is stored in the file. */
  386.  
  387.         Prefix[FreeCode] = OldCode;
  388.         Suffix[FreeCode] = FinChar;
  389.         OldCode = InCode;
  390.  
  391. /* Point to the next slot in the table.  If we exceed the current
  392.  * MaxCode value, increment the code size unless it's already 12.  If it
  393.  * is, do nothing: the next code decompressed better be CLEAR
  394.  */
  395.  
  396.         FreeCode++;
  397.         if (FreeCode >= MaxCode) {
  398.         if (CodeSize < 12) {
  399.             CodeSize++;
  400.             MaxCode *= 2;
  401.             ReadMask = (1 << CodeSize) - 1;
  402.         }
  403.         }
  404.     }
  405.     Code = ReadCode();
  406.     }
  407.  
  408.     free(Raster);
  409.  
  410.     if (Verbose)
  411.     fprintf(stderr, "done.\n");
  412.  
  413.     if (fp != stdin)
  414.     fclose(fp);
  415.  
  416.     if (outf == NULL || strcmp(outf, "Standard Output") == 0) {
  417.     outf = "Standard Output";
  418.     fp = stdout;
  419.     }
  420.     else {
  421.     if (!(fp = fopen(outf, "w")))
  422.         pm_error( "%s couldn't be opened for writing", outf, 0,0,0,0 );
  423.     }
  424.  
  425.     if (Verbose)
  426.     fprintf(stderr, "Writing rasterfile in %s...", outf);
  427.  
  428. #ifdef notdefSS
  429. /* SS: Pixrect Rasterfile output code. */
  430.     if (pr_dump(Output, fp, &Colormap, RT_STANDARD, 0) == PIX_ERR)
  431.     pm_error( "error writing Sun Rasterfile: %s", outf, 0,0,0,0 );
  432. #endif notdefSS
  433. /* PPMS: PPM output code. */
  434.     ppm_writeppm(stdout, pixels, Width, Height, (pixval) 255);
  435.  
  436.     if (Verbose)
  437.     fprintf(stderr, "done.\n");
  438.  
  439. #ifdef notdefSS
  440.     pr_destroy(Output);
  441. #endif notdefSS
  442.  
  443.     if (fp != stdout)
  444.     fclose(fp);
  445.  
  446.     exit(0);
  447. }
  448.  
  449.  
  450. /* Fetch the next code from the raster data stream.  The codes can be
  451.  * any length from 3 to 12 bits, packed into 8-bit bytes, so we have to
  452.  * maintain our location in the Raster array as a BIT Offset.  We compute
  453.  * the byte Offset into the raster array by dividing this by 8, pick up
  454.  * three bytes, compute the bit Offset into our 24-bit chunk, shift to
  455.  * bring the desired code to the bottom, then mask it off and return it. 
  456.  */
  457. ReadCode()
  458. {
  459. int RawCode, ByteOffset;
  460.  
  461.     ByteOffset = BitOffset / 8;
  462.     RawCode = Raster[ByteOffset] + (0x100 * Raster[ByteOffset + 1]);
  463.     if (CodeSize >= 8)
  464.     RawCode += (0x10000 * Raster[ByteOffset + 2]);
  465.     RawCode >>= (BitOffset % 8);
  466.     BitOffset += CodeSize;
  467.     return(RawCode & ReadMask);
  468. }
  469.  
  470.  
  471. AddToPixel(Index)
  472. unsigned char Index;
  473. {
  474. #ifdef notdefSS
  475.     *(Image + YC * BytesPerScanline + XC) = Index;
  476. #endif notdefSS
  477. /* PPMS: Store a pixel. */
  478.     if (YC < Height)
  479.     PPM_ASSIGN(pixels[YC][XC], Red[Index], Green[Index], Blue[Index]);
  480.  
  481. /* Update the X-coordinate, and if it overflows, update the Y-coordinate */
  482.  
  483.     if (++XC == Width) {
  484.  
  485. /* If a non-interlaced picture, just increment YC to the next scan line. 
  486.  * If it's interlaced, deal with the interlace as described in the GIF
  487.  * spec.  Put the decoded scan line out to the screen if we haven't gone
  488.  * past the bottom of it
  489.  */
  490.  
  491.     XC = 0;
  492.     if (!Interlace) YC++;
  493.     else {
  494.         switch (Pass) {
  495.         case 0:
  496.             YC += 8;
  497.             if (YC >= Height) {
  498.             Pass++;
  499.             YC = 4;
  500.             }
  501.         break;
  502.         case 1:
  503.             YC += 8;
  504.             if (YC >= Height) {
  505.             Pass++;
  506.             YC = 2;
  507.             }
  508.         break;
  509.         case 2:
  510.             YC += 4;
  511.             if (YC >= Height) {
  512.             Pass++;
  513.             YC = 1;
  514.             }
  515.         break;
  516.         case 3:
  517.             YC += 2;
  518.         break;
  519.         default:
  520.         pm_error( "can't happen", 0,0,0,0,0 );
  521.         }
  522.     }
  523.     }
  524. }
  525.