www.pudn.com > ilib > IGIF.c
/* * IGIF.c * * Image library * * Description: * Portable routines to manipulate raster images. * * Previous version of Ilib used code from pbmplus to save GIF images. * Due to copyright issues with encoding GIFs, the GIF encoding routines * have been removed. Instead we use Giflib which is freely available at: * http://prtr-13.ucsc.edu/~badger/software/giflib.shtml * * Libungif is available at: * http://prtr-13.ucsc.edu/~badger/software/libungif.shtml * * Note that you may use giflib or libungif. They are basically the same * library except that libungif does not include the questionable LZW * compression algorithm (that's subject to the Unisys copyright.) Thus, * GIFs from libungif will be uncompressed (larger). * * Thanks to Eric S. Raymond and Gershon Elber for making Giflib available. * * History: * 20-Aug-99 Craig Knudsen cknudsen@radix.net * Support writing transparent colors. * 19-Aug-99 Craig Knudsen cknudsen@radix.net * Updated to support writing interlaced GIF files. * Added support for reading/writing comments. * Added support for reading transparent color. * 18-Aug-99 Craig Knudsen cknudsen@radix.net * Updated to support GIF extensions (like interlace) * when reading files. * 17-May-98 Craig Knudsen cknudsen@radix.net * Updated to use Giflib instead of doing or own * GIF encoding. Saves me from getting sued ;-) * 20-May-96 Craig Knudsen cknudsen@radix.net * Created * ****************************************************************************/ #ifdef HAVE_GIFLIB #include#include #include #include #define PROGRAM_NAME "Ilib" #include #include "Ilib.h" #include "IlibP.h" #define colors_match(color,r,g,b) \ (color->red == r && color->green == g && color->blue == b ) #define MAX_COLORMAP_SIZE (256) #define ABS(a) (a < 0 ? (0-a) : a ) static int InterlacedOffset[] = { 0, 4, 2, 1 }; static int InterlacedJumps[] = { 8, 8, 4, 2 }; static int color_compare ( r, g, b, test1, test2 ) unsigned int r, g, b; IColorP *test1, *test2; { int diff1, diff2; diff1 = ABS ( (int)r - (int)test1->red ) + ABS ( (int)g - (int)test1->green ) + ABS ( (int)b - (int)test1->blue ); diff2 = ABS ( (int)r - (int)test2->red ) + ABS ( (int)g - (int)test2->green ) + ABS ( (int)b - (int)test2->blue ); return ( diff1 > diff2 ); } IError _IWriteGIF ( fp, image, options ) FILE *fp; IImageP *image; IOptions options; { int r, c, offset; unsigned char *ptr; unsigned int red, green, blue; IColorP *colormap[MAX_COLORMAP_SIZE]; ColorMapObject *GIFcolormap; int num_colors = 0, color_ceil, loop, loop2, closest, bits_per_pixel; int color_found; int transparent = -1, interlaced = 0; int fd; GifFileType *gft; GifByteType ext[4]; unsigned char *data; /* first make an 8-bit version of the image */ data = (unsigned char *) calloc ( image->width * image->height, sizeof ( unsigned char ) ); memset ( data, '\0', sizeof ( image->width * image->height ) ); /* Reduce to 256 colors ** This is a god-awful hack of an algorithm. Should really use a ** better one. ** The first 256 colors found will be used, the rest will be converted ** to closest. ** NOTE: we should change this to call QuantizeBuffer in GIFLIB. */ for ( r = 0; r < image->height; r++ ) { for ( c = 0; c < image->width; c++ ) { offset = ( r * image->width ) + c; if ( image->greyscale ) { ptr = image->data + ( r * image->width ) + c; red = green = blue = (unsigned int) *ptr; } else { ptr = image->data + ( r * image->width * 3 ) + ( c * 3 ); red = (unsigned int) *ptr; green = (unsigned int) *( ptr + 1 ); blue = (unsigned int) *( ptr + 2 ); } color_found = 0; for ( loop = 0; loop < num_colors; loop++ ) { if ( colors_match ( colormap[loop], red, green, blue ) ) { data[offset] = loop; color_found = 1; break; } } if ( ! color_found ) { if ( num_colors < MAX_COLORMAP_SIZE ) { colormap[num_colors] = (IColorP *) malloc ( sizeof ( IColorP ) ); memset ( colormap[num_colors], '\0', sizeof ( IColorP ) ); colormap[num_colors]->magic = IMAGIC_COLOR; colormap[num_colors]->red = red; colormap[num_colors]->green = green; colormap[num_colors]->blue = blue; data[offset] = num_colors; num_colors++; } else { // find closest! closest = 0; for ( loop = 0; loop < num_colors; loop++ ) { if ( color_compare ( red, green, blue, colormap[closest], colormap[loop] ) < 0 ) closest = loop; } } } } } /* how many cells in colormap (eg. 28->32, 55->64, etc.) */ for ( bits_per_pixel = 1, color_ceil = 2; color_ceil < num_colors; color_ceil *= 2, bits_per_pixel++ ) ; fd = fileno ( fp ); GIFcolormap = MakeMapObject ( color_ceil, NULL ); for ( loop = 0; loop < color_ceil; loop++ ) { if ( loop < num_colors ) { GIFcolormap->Colors[loop].Red = colormap[loop]->red; GIFcolormap->Colors[loop].Green = colormap[loop]->green; GIFcolormap->Colors[loop].Blue = colormap[loop]->blue; } else { GIFcolormap->Colors[loop].Red = GIFcolormap->Colors[loop].Green = GIFcolormap->Colors[loop].Blue = 0; } } gft = EGifOpenFileHandle ( fileno ( fp ) ); /* causes seg fault... EGifSetGifVersion ( "89a" ); */ if ( options & IOPTION_INTERLACED ) interlaced = 1; if ( image->transparent ) { for ( loop = 0; loop < num_colors; loop++ ) { if ( colors_match ( colormap[loop], image->transparent->red, image->transparent->green, image->transparent->blue ) ) { transparent = loop; break; } } } else transparent = -1; if ( EGifPutScreenDesc ( gft, image->width, image->height, bits_per_pixel, 0, GIFcolormap ) == GIF_ERROR ) return ( IGIFError ); if ( image->comments ) EGifPutComment ( gft, image->comments ); if ( transparent >= 0 ) { ext[0] = 1; ext[1] = 0; ext[2] = 0; ext[3] = transparent; EGifPutExtension ( gft, GRAPHICS_EXT_FUNC_CODE, 4, ext ); } if ( EGifPutImageDesc ( gft, 0, 0, image->width, image->height, interlaced, NULL ) == GIF_ERROR ) return ( IGIFError ); /* for interlaced images, we need to write the rows in a different order */ if ( interlaced ) { for ( loop = 0; loop < 4; loop++ ) { for ( loop2 = InterlacedOffset[loop]; loop2 < image->height; loop2 += InterlacedJumps[loop] ) { if ( EGifPutLine ( gft, data + ( loop2 * image->width ), image->width ) == GIF_ERROR ) return ( IGIFError ); } } } else { /* Write the data all at once */ if ( EGifPutLine ( gft, data, image->width * image->height ) == GIF_ERROR ) return ( IGIFError ); } EGifCloseFile ( gft ); /* free up allocated resources */ free ( data ); for ( loop = 0; loop < num_colors; loop++ ) { free ( colormap[loop] ); } FreeMapObject ( GIFcolormap ); return ( INoError ); } IError _IReadGIF ( fp, options, image_return ) FILE *fp; IOptions options; IImageP **image_return; { IImageP *image = NULL; GifFileType *gft; GifRecordType rt; GifPixelType *gifdata = NULL; GifByteType *extension; int extcode; int fd; int loop, loop2, col; unsigned char *ptr, *r, *g, *b; unsigned int temp; char *comments = NULL; unsigned int transparent_ind; int trans_set = 0; IColor transcolor; fd = fileno ( fp ); if ( ( gft = DGifOpenFileHandle ( fd ) ) == NULL ) return ( IGIFError ); while ( image == NULL ) { rt = UNDEFINED_RECORD_TYPE; if ( DGifGetRecordType ( gft, &rt ) == GIF_ERROR ) return ( IGIFError ); if ( rt == IMAGE_DESC_RECORD_TYPE ) { if ( DGifGetImageDesc ( gft ) == GIF_ERROR ) return ( IGIFError ); image = (IImageP *) ICreateImage ( gft->Image.Width, gft->Image.Height, IOPTION_NONE ); gifdata = (GifPixelType *) malloc ( image->width * image->height ); /* we read the lines out of order for interlaced images (yuck) */ if ( gft->Image.Interlace ) { for ( loop = 0; loop < 4; loop++ ) { for ( loop2 = InterlacedOffset[loop]; loop2 < image->height; loop2 += InterlacedJumps[loop] ) { if ( DGifGetLine ( gft, gifdata + ( loop2 * image->width ), image->width ) == GIF_ERROR ) return ( IGIFError ); } } } else { if ( DGifGetLine ( gft, gifdata, image->width * image->height ) == GIF_ERROR ) return ( IGIFError ); } /* convert to a 24-bit image */ for ( loop = 0; loop < image->height; loop++ ) { for ( col = 0; col < image->width; col++ ) { ptr = gifdata + ( loop * image->width ) + col; temp = *ptr; r = image->data + ( loop * image->width * 3 ) + ( col * 3 ); g = r + 1; b = r + 2; if ( temp > gft->SColorMap->ColorCount ) { temp = gft->SColorMap->ColorCount - 1; fprintf ( stderr, "ILib Warning: Invalid color found in GIF.\n" ); } *r = gft->SColorMap->Colors[temp].Red; *g = gft->SColorMap->Colors[temp].Green; *b = gft->SColorMap->Colors[temp].Blue; } } } else if ( rt == EXTENSION_RECORD_TYPE ) { /* ignore all extensions except comments */ DGifGetExtension ( gft, &extcode, &extension ); while ( extension != NULL ) { if ( extcode == COMMENT_EXT_FUNC_CODE ) { if ( comments != NULL ) free ( comments ); comments = (char *) malloc ( strlen ( extension + 1 ) + 1 ); strcpy ( comments, extension + 1 ); } else if ( extcode == GRAPHICS_EXT_FUNC_CODE ) { /* this is used to set transparent color index */ if ( extension[1] & 0x01 ) { trans_set = 1; transparent_ind = extension[4]; } } else { /* fprintf ( stderr, "Ignoring unknown extension: %d\n", extension[0] ); */ } DGifGetExtensionNext ( gft, &extension ); } } } /* lookup transparent color from colormap */ if ( trans_set ) { transcolor = IAllocColor ( gft->SColorMap->Colors[transparent_ind].Red, gft->SColorMap->Colors[transparent_ind].Green, gft->SColorMap->Colors[transparent_ind].Blue ); ISetTransparent ( image, transcolor ); } image->comments = comments; if ( gifdata ) free ( gifdata ); DGifCloseFile ( gft ); *image_return = image; return ( INoError ); } #endif /* HAVE_GIFLIB */