jedioutcast/utils/roq2/libim/imgif.c

4068 lines
65 KiB
C
Raw Normal View History

2013-04-04 18:02:27 +00:00
/**
** $Header: /roq/libim/imgif.c 1 11/02/99 4:38p Zaphod $
** Copyright (c) 1989-1995 San Diego Supercomputer Center (SDSC)
** a division of General Atomics, San Diego, California, USA
**
** Users and possessors of this source code are hereby granted a
** nonexclusive, royalty-free copyright and design patent license to
** use this code in individual software. License is not granted for
** commercial resale, in whole or in part, without prior written
** permission from SDSC. This source is provided "AS IS" without express
** or implied warranty of any kind.
**
** For further information contact:
** E-Mail: info@sds.sdsc.edu
**
** Surface Mail: Information Center
** San Diego Supercomputer Center
** P.O. Box 85608
** San Diego, CA 92138-5608
** (619) 534-5000
**/
#define HEADER " $Header: /roq/libim/imgif.c 1 11/02/99 4:38p Zaphod $"
/**
** FILE
** imgif.c - Compuserve Graphics Interchange Format
**
** PROJECT
** libim - SDSC image manipulation library
**
** DESCRIPTION
** imgif.c contains routines to read and write Compuserve GIF image
** format for the image manipulation library.
**
** PUBLIC CONTENTS
** d =defined constant
** f =function
** m =defined macro
** t =typedef/struct/union
** v =variable
** ? =other
**
** none
**
** PRIVATE CONTENTS
** imGifHeaderInfo t GIF header information
** imGifHeaderFields v imGifHeaderInfo description for Bin pkg
** imGifHeader v Gif header holder
** imGifImDescInfo t Gif image descriptor information
** imGifImDescFields v imGifImDescInfo description for Bin pkg
** imGifImDesc v Gif image descriptor holder
**
** IMGIFMAGIC d file magic number
** IMGIFBITPIXEL d mask to read #bits/pixel in the image
** IMGIFGLOBALCM d mask to read if a global colormap is present
** IMGIFLOCALCM d mask to read if a local colormap is present
** IMGIFINTERLACED d mask to read the pixel display sequence used
** IMGIFTERMINATOR d termination character of a Gif image file
** IMGIFEXTENSION d Gif extension block introducer character
** IMGIFSEPARATOR d image separator character
** IMGIFENDBLOCK d terminates extension blocks
** IMGIFNCOLORRES d # bits of color resolution
** IMGIFNBITSPIXEL d # bits per pixel
** IMGIFBACKGROUND d index of background color
** IMGIFCODESIZE d initial code size used for lzw algorithm
** IMGIFNOCOLORMAP d no color lookup table present
**
** imGifVfbRead1 f Read a mono Gif image
** imGifCltRead f Read a color lookup table
** imGifCltWrite f Write a color lookup table
** imGifVfbWrite8 f Write a 8-index Vfb
** imGifVfbWrite1 f Write a mono Vfb
** imGifRead f read a Compuserve GIF image
** imGifWrite f write a Compuserve GIF image
**
** HISTORY
** $Log: /roq/libim/imgif.c $
*
* 1 11/02/99 4:38p Zaphod
** Revision 1.22 1995/06/29 00:28:04 bduggan
** updated copyright year
**
** Revision 1.21 1995/06/15 20:19:28 bduggan
** removed use of 'new'
** Added some casts
**
** Revision 1.20 1995/05/30 16:57:24 bduggan
** Changed comment.
**
** Revision 1.19 1995/05/17 23:44:54 bduggan
** Added transparency support
**
** Revision 1.18 1995/04/03 21:24:38 bduggan
** took out #ifdef NEWMAGIC
**
** Revision 1.17 1995/01/10 23:23:37 bduggan
** Made read/write functions static
**
** Revision 1.17 1995/01/10 23:23:37 bduggan
** Made read/write functions static
**
** Revision 1.16 94/10/03 11:30:06 nadeau
** Updated to ANSI C and C++ compatibility.
** Removed all use of register keyword.
** Minimized use of custom SDSC types (e.g., uchar vs. unsigned char)
** Changed all float arguments to double.
** Added forward declarations.
** Added misc. casts to passify SGI and DEC compilers.
** Changed all macros and defined constants to have names
** starting with IM.
** Rearranged magic number structures for format handlers.
** Made format handler routines static (i.e., local to file).
** Updated comments, adding format descriptions and references.
** Updated indenting on some code.
** Updated copyright message.
**
** Revision 1.15 92/12/09 18:33:05 nadeau
** Corrected misinterpretation of CLT write flags that wrote
** grayscale INDEX8 images incorrectly. Also corrected and
** updated various info messages.
**
** Revision 1.14 92/12/03 01:48:12 nadeau
** Corrected info messages.
**
** Revision 1.13 92/11/23 18:42:14 nadeau
** Removed use of IMINFOMSG.
**
** Revision 1.12 92/11/04 11:49:03 groening
** put ImFIleFormat info and magic number info
** from imfmt.c into this file.
**
** Revision 1.11 92/10/12 15:57:13 vle
** Fixed typo: Limpel -> Lempel
**
** Revision 1.10 92/09/29 17:58:50 vle
** Added ImInfo messages.
**
** Revision 1.9 92/09/17 14:42:03 vle
** Updated to 89a specification. Fixed bug in 2-bit image read.
** Updated copyright notice.
**
** Revision 1.8 92/04/03 17:43:14 nadeau
** Added extern declarations of functions so that the SGI
** compiler and linker don't get confused.
**
** Revision 1.7 91/10/03 09:03:01 nadeau
** Fixed #includes. Turned off DEBUG.
**
** Revision 1.6 91/03/13 17:09:44 soraya
** fixing bugs
**
** Revision 1.5 91/03/11 09:16:40 soraya
** Optimization, finish of write, and removal of debug
** print statements.
**
** Revision 1.4 91/02/12 11:45:52 nadeau
** Removed the tag table checking and VFB conversion now
** handled by ImFileRead and ImFileWrite. Made the error
** checking more robust.
**
** Revision 1.3 91/01/31 08:34:20 soraya
** *** empty log message ***
**
** Revision 1.2 90/12/26 18:50:20 soraya
** *** empty log message ***
**
** Revision 1.1 90/12/26 13:24:43 soraya
** Initial revision
**
**/
#include "iminternal.h"
/**
** FORMAT
** gif - Compuserve Graphics Interchange Format
**
** AKA
** giff
**
** FORMAT REFERENCES
** Graphics Interchange Format, version 89a, CompuServe, 1990.
**
** Bit-Mapped Graphics, Steve Rimmer, McGraw-Hill, 1990.
**
** Graphics File Formats, David C. Kay, John R. Levine, McGraw-Hill,
** 1992.
**
** Supercharged Bitmapped Graphics, Steve Rimmer, McGraw-Hill, 1992.
**
** CODE CREDITS
** Custom development, Soraya Gonzales, Intevep S.A., Venzuela, 1990.
** Extensions, Vinh Le, San Diego Supercomputer Center, 1992.
** Extensions, Dave Nadeau, San Diego Supercomputer Center, 1993.
**
** DESCRIPTION
** CompuServe's GIF (Graphics Interchange Format) is used on on-line
** image libraries throughout the world for the storage of medium and
** low resolution images of 8-bit depths or less.
**
** Header Block
** GIF data streams begin with a header block of the form:
**
** Field Size
** ----- ----
** Signature 3 bytes
** Version 3 bytes
**
** The "Signature" field is always "GIF". The "Version" field is
** one of "87a" or "89a", corresponding to the two outstanding versions
** of the GIF format specification.
**
** This GIF translator reads 87a and 89a spec GIF files, but
** per recommendations in the GIF spec, always writes the
** lowest-common-denominator GIF files at 87a spec levels.
**
** Logical Screen Block
** Immediately following the GIF header is a logical screen block of
** the form:
**
** Field Size
** ----- ----
** Logical Screen Width 2 bytes, unsigned integer
** Logical Screen Height 2 bytes, unsigned integer
** Flags 1 byte
** Background Color Index 1 byte
** Pixel Aspect Ratio 1 byte
**
** The screen width and height refer to the size of the logical screen
** onto which the GIF image is to be displayed on the display device.
**
** This GIF translator ignores the screen width and height.
**
** The "Flags" field is a bitmask with the following bit meanings:
**
** Field Bits
** ----- ----
** Global Color Table 7
** Color Resolution 6,5,4
** Sort 3
** Size of Global Color Tb 2,1,0
**
** The "Global Color Table" flag indicates if the file has a global
** color table. If so, it'll immediately follow the Logical Screen
** Block in the file. Bit 7 = 0 = no global color table. Bit 7 = 1 =
** there is a global color table.
**
** This GIF translator can read global color tables, but the
** writer does not generate them. The writer uses local color
** tables instead.
**
** The "Color Resolution" flag gives the number of bits per primary color,
** minus 1. A value of 7 means 8-bits per primary and is typical.
**
** This GIF translator can read color table resolutions up to
** 8-bits per channel and converts them internally to ImClt's
** with 8-bits per channel. GIF files written by this
** translator are always 8-bits per channel.
**
** The "Sort" flag indicates if the global color table (if there is one)
** is sorted from most to least important color. Bit 3 = 0 = unsorted.
** Bit 3 = 1 = sorted.
**
** This GIF translator ignores color table sorting on reading.
** On writing, the color table is always unsorted.
**
** The "Global Color Table Size" field is used to calculate the length of
** the global color table. Take 2 and raise it to a power equal to this
** field plus 1. So, a value of 7 for the field gives 2^(7+1) entries,
** or 2^8 = 256 entries. Even when there is no global color table, this
** field should be set to the size of a typical color table to help
** future display routines.
**
** This GIF translator uses this field on reading to determine
** the global color table size, and sets this field on writing
** to an appropriate value.
**
** The "Background Color Index" field is an index into the global
** color table (if the table is present) and selects the color to use
** for background pixels not covered by image data from the GIF file.
** If there is no global color table, this field should be 0.
**
** This GIF translator doesn't use the background color on
** reading, and sets it to 0 on writing.
**
** The "Pixel Aspect Ratio" field gives the aspect ratio of pixels in
** the image and is meant to account for non-square pixels on crude
** graphics systems. A value of 0 means no aspect ratio information is
** provided in the field.
**
** This GIF translator ignores the pixel aspect ratio on reading,
** and sets it to 0 on writing.
**
** Global Color Table Block
** The global color table is a default color table for images in the file
** that don't have a local color table associated with them. This block
** is only present if the global color table flag is set in the Logical
** Screen block above.
**
** If present, this block will contain a number of bytes equal to:
**
** 3 * 2^(table size + 1)
**
** where the "table size" is given in the Logical Screen Block above.
**
** Color table values are in the order RGB, RGB, RGB, one byte per channel,
** from color table entry 0 to the maximum. The maximum number of entries
** allowed by the spec is 256.
**
** This GIF translator handles global color tables on reading
** image files, but never generates them on writing. All
** written images are provided with local color tables.
**
** Image Descriptor
** The image descriptor describes a raster image in the GIF file. There
** will be one image descriptor for each such image, with no upper limit
** on the number of images contained within a single GIF file.
**
** Image descriptions begin with an image descriptor block of the form:
**
** Field Size
** ----- ----
** Separator 1 byte
** Left Position 2 bytes, unsigned integer
** Top Position 2 bytes, unsigned integer
** Width 2 bytes, unsigned integer
** Height 2 bytes, unsigned integer
** Flags 1 byte
**
** The image separator marks the beginning of the descriptor and helps
** to separate images from each other in a multi-image file. The
** separator always contains the hex value "0x2C".
**
** The image left and top position values are relative to the logical
** screen, as defined in the logical screen block. (0,0) is at the
** top left of the logical screen.
**
** This GIF translator does not pay attention to logical screens.
** So, image left-top positions within such are ignored. On
** writing images, the left and top position values are always
** set to 0.
**
** The width and height fields give the image size in pixels.
**
** The "Flags" field is a bitmask with the following bit meanings:
**
** Field Bits
** ----- ----
** Local Color Table 7
** Interlace 6
** Sort 5
** Reserved 4,3
** Size of Local CLT 2,1,0
**
** The "Local Color Table" flag indicates if there is a local CLT
** immediately following the image descriptor. Bit 7 = 1 = there is
** a local CLT. Bit 7 = 0 = there isn't.
**
** This GIF translator handles local color tables with read
** images. Written images always have a local CLT if the
** VFB to be written had one. Grayscale VFB's without CLTs
** are written without local CLTs.
**
** The "Interlace" flag indicates if the image is interlaced using a
** 4-pass interlace pattern. Interlacing is discussed later on in this
** comment block. Bit 6 = 1 = the image is interlaced. Bit 6 = 0 =
** it isn't interlaced.
**
** This GIF translator handles interlaced incoming images,
** but only writes non-interlaced images.
**
** The "Sort" flag indicates if the local color table (if there is one)
** is sorted from most to least important color. Bit 5 = 0 = unsorted.
** Bit 5 = 1 = sorted.
**
** This GIF translator ignores color table sorting on reading.
** On writing, the color table is always unsorted.
**
** The "Size of Local Color Table" field is used to calculate the length of
** the local color table. Take 2 and raise it to a power equal to this
** field plus 1. So, a value of 7 for the field gives 2^(7+1) entries,
** or 2^8 = 256 entries. If there is no local CLT, this field should be
** set to 0.
**
** This GIF translator uses this field on reading to determine
** the local color table size, and sets this field on writing
** to an appropriate value.
**
** Local Color Table Block
** If an image has a local color table (see the "Flags" field of the
** image descriptor block), then a description of that table immediate
** follows the image descriptor for the image. This local CLT overrides
** any global CLT for this image only.
**
** If present, this block will contain a number of bytes equal to:
**
** 3 * 2^(table size + 1)
**
** where the "table size" is given in the Image Descriptor Block above.
**
** Color table values are in the order RGB, RGB, RGB, one byte per channel,
** from color table entry 0 to the maximum. The maximum number of entries
** allowed by the spec is 256.
**
** This GIF translator reads local CLTs as needed. On writing
** images, local CLTs are generated for VFBs with CLTs.
** Grayscale VFBs without CLTs are written without a local CLT.
**
** Image Data Block
** The image data for an image follows immediately after the local CLT
** (if any) following the image descriptor for the image. Image data is
** always compressed using a Limpel-Ziv & Welch (LZW) compression scheme
** described below.
**
** GIF Trailer
** The GIF data stream is terminated with a GIF trailer block of the
** form:
**
** Field Size
** ----- ----
** GIF Trailer 1 byte
**
** The "GIF Trailer" byte always has the hex value "0x3B".
**
** Extensions
** Various extensions are defined by the GIF spec. These extensions
** allow the embedding of various display and application-specific
** controls. All extensions begin with the following three fields:
**
** Field Size
** ----- ----
** Extension Introducer 1 byte
** Extension Label 1 byte
** Extension Block Size 1 byte
**
** The "Extension Introducer" is a flag indicating the start of an
** extension block and always has the hex value "0x21".
**
** The "Extension Label" indicates the type of extension. The 1989 spec
** defines the following extension labels:
**
** Extension Label Meaning
** --------------- -------
** 0xF9 Graphic Control Extension
** 0xFE Comment Extension
** 0x01 Plain Text Extension
** 0xFF Application Extension
**
** The "Extension Block Size" field gives the size, in bytes, of the
** extension block.
**
** This GIF translator ignores all extensions on reading GIF
** files, and generates no extensions when writing them.
**
** Image Data Interlacing
** The "Interlace" flag in the "Flags" field of an Image Descriptor
** Block indicates whether the following image data is interlaced or
** not. The basic idea is to order the scanlines in the image data
** so that widely separated scanlines come first, and then we fill in
** the ones we missed. This scheme was designed to allow quick previewing
** of an incomming GIF file by seeing some of the scanlines, all over
** the image, first. Then, if you didn't want to continue reading the
** GIF file you could cancel the read and save time by not bother with
** reading the rest in. Although this idea was meant for slower technology,
** it has proved useful once more, since transmission of data from one
** corner of the internet to the other is not instanataneous, and programs
** such as netScape display the image as it is received.
**
** So, we generate interlaced gif's.
**
** When interlacing is enabled, the image data is grouped into 4 passes:
**
** Pass Contains
** ---- --------
** 1 Every 8th row, starting with row 0
** 2 Every 8th row, starting with row 4
** 3 Every 4th row, starting with row 2
** 4 Every 2nd row, starting with row 1
**
** When interlacing is turned off, the image data instead contains
** image rows in sequential order from row 0 (top) to the last row.
**/
/*
* FUNCTION DECLARATIONS
*/
#ifdef __STDC__
static int imGifVfbRead1(int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable, ImVfb **pVfb,
unsigned char interlaced,unsigned char codesize );
static int imGifVfbRead8(int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable, ImVfb **pVfb,
unsigned char interlaced, unsigned char codesize );
static int imGifNBitsPixel (int ncolors,int *);
static int imGifRead( int ioType, int fd, FILE *fp,TagTable *flagsTable, TagTable *tagTable);
static int imGifWrite( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable );
static int imGifWriteTransparency( int ioType, int fd, FILE* fp, int transValue);
#else
static int imGifVfbRead1( );
static int imGifVfbRead8( );
static int imGifNBitsPixel( );
static int imGifRead( );
static int imGifWrite( );
static int imGifWriteTransparency( );
#endif
/*
* FORMAT INFORMATION
* imGifNames - format's name and aliases
* imGifReadMap - read attributes
* imGifWriteMap - write attributes
* imGifMagicNumber - magic number
* imGifMagic - list of magic numbers
* ImFileGifFormat - master format description
*/
static char *imGifNames[ ] = { "gif", "giff", NULL };
static ImFileFormatReadMap imGifReadMap[ ] =
{
/* in out */
/* type,ch,dep, attr. VFB type attr. */
{ IN,1,1, LZW, IMVFBMONO, 0 },
{ IN,1,2, LZW, IMVFBINDEX8, 0 },
{ IN,1,3, LZW, IMVFBINDEX8, 0 },
{ IN,1,4, LZW, IMVFBINDEX8, 0 },
{ IN,1,5, LZW, IMVFBINDEX8, 0 },
{ IN,1,6, LZW, IMVFBINDEX8, 0 },
{ IN,1,7, LZW, IMVFBINDEX8, 0 },
{ IN,1,8, LZW, IMVFBINDEX8, 0 },
{ IN,1,1, LZW|C, IMVFBMONO, C },
{ IN,1,2, LZW|C, IMVFBINDEX8, C },
{ IN,1,3, LZW|C, IMVFBINDEX8, C },
{ IN,1,4, LZW|C, IMVFBINDEX8, C },
{ IN,1,5, LZW|C, IMVFBINDEX8, C },
{ IN,1,6, LZW|C, IMVFBINDEX8, C },
{ IN,1,7, LZW|C, IMVFBINDEX8, C },
{ IN,1,8, LZW|C, IMVFBINDEX8, C },
{ -1, 0, -1, 0 },
};
static ImFileFormatWriteMap imGifWriteMap[ ] =
{
/* in out */
/* VFB type, attr., type,ch,dep, attr., func */
{ IMVFBMONO, C|A, IN,1,1, LZW|C|A,imGifWrite },
{ IMVFBMONO, C, IN,1,1, LZW|C, imGifWrite },
{ IMVFBINDEX8, C|A, IN,1,8, LZW|C|A,imGifWrite },
{ IMVFBINDEX8, C, IN,1,8, LZW|C, imGifWrite },
{ -1, 0, -1, 0, NULL },
};
static unsigned char imGifMagicNumber87[ ] = { 'G', 'I', 'F', '8', '7', 'a' };
static unsigned char imGifMagicNumber89[ ] = { 'G', 'I', 'F', '8', '9', 'a' };
static ImFileMagic imFileGifMagic[ ] =
{
{ 0, 6, imGifMagicNumber87 },
{ 0, 6, imGifMagicNumber89 },
{ 0, 0, NULL },
};
ImFileFormat ImFileGifFormat =
{
imGifNames, /* Names */
"Graphics Image File", /* Description */
"Compuserve", /* Creator */
"1- thru 8-bit color index Lempel-Ziv & Welch-compressed single- or\n\
multi-image files.", /* Read support */
"1 and 8-bit color index Lempel-Ziv & Welch-compressed single- or\n\
multi-image files.", /* Write support*/
imFileGifMagic, /* Magic #'s */
IMMULTI, IMPIPE, /* Read? */
IMMULTI, IMPIPE, /* Write? */
imGifRead, imGifReadMap, imGifWriteMap, /* Maps */
};
/*
* TYPEDEF & STRUCTURE
* imGifHeaderInfo - Gif header information
* imGifHeaderFields - Gif Info description for Bin pkg
* imGifHeader - Gif header holder
*
* DESCRIPTION
* The imGifHeaderInfo includes the GIF Header block and Logical Screen
* block fields.
*/
typedef struct imGifHeaderInfo
{
unsigned char gif_magic[6]; /* GIF signature + version */
sdsc_uint16 gif_width; /* Screen width */
sdsc_uint16 gif_height; /* Screen height */
unsigned char gif_flags; /* Assorted flags */
unsigned char gif_background; /* Color index of screen background*/
unsigned char gif_aspect; /* Pixel aspect ratio */
} imGifHeaderInfo;
static imGifHeaderInfo imGifHeader; /* GIF file header */
static BinField imGifHeaderFields[ ] =
{
{ UCHAR, 1, 6 }, /* gif_magic */
{ UINT16, 2, 1 }, /* gif_width */
{ UINT16, 2, 1 }, /* gif_height */
{ UCHAR, 1, 1 }, /* gif_flags */
{ UCHAR, 1, 1 }, /* gif_background */
{ UCHAR, 1, 1 }, /* gif_aspect */
{ 0, 0, 0 }
};
/*
* TYPEDEF & STRUCTURE
* imGifImDescInfo - Gif image descriptor information
* imGifImDescFields - imGifImDescInfo description for Bin pkg
* imGifImDesc - Gif image descriptor holder
*
* DESCRIPTION
* The image descriptor defines the actual placement and extents of the
* following image within the space defined by screen width and height.
* Also defined are flags to indicate the presence of a local color
* lookup map, the pixel display sequence and the #bits per pixel
* defined for this image.
*
*/
typedef struct imGifImDescInfo
{
sdsc_uint16 im_left; /* Start of image from left side*/
sdsc_uint16 im_top; /* Start of image from top */
sdsc_uint16 im_width; /* Width of the image in pixels */
sdsc_uint16 im_height; /* Height of the image in pixels*/
unsigned char im_flags; /* Assorted flags */
}imGifImDescInfo;
static imGifImDescInfo imGifImDesc; /* GIF image descriptor */
static BinField imGifImDescFields[] =
{
{ UINT16, 2, 1 }, /* im_left */
{ UINT16, 2, 1 }, /* im_top */
{ UINT16, 2, 1 }, /* im_width */
{ UINT16, 2, 1 }, /* im_height */
{ UCHAR, 1, 1 }, /* im_flags */
{ 0, 0, 0 }
};
/*
* TYPEDEF
* imGifControlBlock - Gif Control Block structure
*
* DESCRIPTION
* This is the structure that is contained in an graphics control
* block. We just use it to set the transparency field.
*/
typedef struct imGifControlBlock
{
unsigned char blocksize; /* always 4 */
unsigned char flags; /* what's in the block */
sdsc_uint16 delay; /* how long to wait for a keypress */
unsigned char transparent_color; /* The color that is transparent */
unsigned char terminator; /* always 0 */
} imGifControlBlock;
static BinField imGifControlBlockFields[] =
{
{ UCHAR, 1, 1}, /* blocksize */
{ UCHAR, 1, 1}, /* flags */
{ UINT16, 2, 1}, /* delay */
{ UCHAR, 1, 1}, /* transparent_color */
{ UCHAR, 1, 1}, /* terminator */
{ 0, 0, 0 }
};
/*
* CONSTANTS
* IMGIFMAGIC87a - file magic number, version 87a
* IMGIFMAGIC89a - file magic number, version 89a
* IMGIFBITPIXEL - mask to read #bits/pixel in the image
* IMGIFGLOBALCM - mask to read if a global colormap is present
* IMGIFLOCALCM - mask to read if a local colormap is present
* IMGIFINTERLACED - mask to read the pixel display sequence used
* IMGIFTERMINATOR - termination character of a Gif image file
* IMGIFEXTENSION - Gif extension block introducer character
* IMGIFSEPARATOR - image separator character
* IMGIFENDBLOCK - terminates extension blocks
* IMGIFNCOLORRES - # bits of color resolution
* IMGIFNBITSPIXEL - # bits per pixel
* IMGIFBACKGROUND - index of background color
* IMGIFCODESIZE - initial code size used for lzw algorithm
* IMGIFNOCOLORMAP - no color lookup table present
*
*/
#define IMGIFMAGIC87a "GIF87a"
#define IMGIFMAGIC89a "GIF89a"
#define IMGIFBITPIXEL 0x07
#define IMGIFGLOBALCM 0x80
#define IMGIFLOCALCM 0x80
#define IMGIFINTERLACED 0x40
#define IMGIFTERMINATOR ';'
#define IMGIFEXTENSION '!'
#define IMGIFSEPARATOR ','
#define IMGIFENDBLOCK 0
#define IMGIFNCOLORRES 8
#define IMGIFNBITSPIXEL 8
#define IMGIFBACKGROUND 0
#define IMGIFCODESIZE IMGIFNBITSPIXEL
#define IMGIFNOCOLORMAP 0
#define IMGIFGRAPHICCONTROL 0xF9
#define IMGIFCOMMENT 0xFE
#define IMGIFPLAINTEXT 0x01
#define IMGIFAPPLICATION 0xFF
#define IMGIFTRANSPARENCY 0x01 /* For graphic control blocks */
/*
* FUNCTION
* imGifCltRead - read a color lookup table
*
* DESCRIPTION
* Used for reading either global or local color tables, this routine
* reads in a a table of nClt colors and stores it into a new ImClt
* which is then returned.
*/
static int /* Returns status */
#ifdef __STDC__
imGifCltRead( int ioType, int fd, FILE *fp, int nClt, ImClt **clt )
#else
imGifCltRead( ioType, fd, fp, nClt, clt )
int ioType; /* Type of I/O to do */
int fd; /* Descriptor to read from */
FILE *fp; /* Pointer to read from */
int nClt; /* # of colors to read */
ImClt **clt; /* Where to put them */
#endif
{
ImCltPtr cptr; /* Color pointer */
unsigned char *cltBuffer; /* Color lookup table buffer */
unsigned char *colorp; /* Color buffer pointer */
int i; /* Counter */
/*
* Allocate a new ImClt and a temporary buffer to store the
* the raw incomming data.
*/
if ( (*clt = ImCltAlloc(nClt)) == IMCLTNULL)
{
ImErrorFatal ( ImQError(), -1, ImErrNo );
}
ImMalloc( cltBuffer, unsigned char *, sizeof( UCHAR ) * nClt * 3 );
/* Read in Red, Green, and Blues. */
if ( ImBinRead( ioType, fd, fp, cltBuffer, UCHAR, 1, nClt * 3 ) == -1 )
{
free( (char *)cltBuffer );
ImReturnBinError( );
}
/* And copy them to the ImClt. */
cptr = ImCltQFirst( *clt );
colorp = cltBuffer;
for ( i = 0; i < nClt; i++ )
{
ImCltSRed( cptr, *(colorp++) );
ImCltSGreen( cptr, *(colorp++) );
ImCltSBlue( cptr, *(colorp++) );
ImCltSInc( *clt, cptr );
}
free( (char *) cltBuffer );
return ( 0 );
}
/*
* FUNCTION
* imGifExtRead - Read the gif extension blocks
*
* DESCRIPTION
* The only extension we pay attention to is image transparency.
* If we see this, we add it to the data table.
*
* Note that we intentionally don't use seek() calls to skip the
* extension data. This allows us to read from pipes.
*/
static int /* Returns status */
#ifdef __STDC__
imGifExtRead( int ioType, int fd, FILE *fp, TagTable* tagTable )
#else
imGifExtRead( ioType, fd, fp, tagTable )
int ioType; /* Type of I/O to do */
int fd; /* Descriptor to read from */
FILE *fp; /* Pointer to read from */
TagTable *tagTable; /* tag table */
#endif
{
unsigned char c, buf[256]; /* Temp buffer */
int transparency=0; /* Is this a transparency block? */
imGifControlBlock block; /* structure with transparency */
char* tmp; /* temporary string for tagtable */
char message[100]; /* buffer for message */
/*
* Read in extension label
*/
if ( ImBinRead( ioType, fd, fp, &c, UCHAR, 1, 1 ) == -1 )
{
ImReturnBinError();
}
/*
* Make sure extension blocks are valid ones
*/
switch( c )
{
case IMGIFGRAPHICCONTROL:
transparency = 1;
break;
case IMGIFCOMMENT:
case IMGIFPLAINTEXT:
case IMGIFAPPLICATION:
break;
default:
ImErrNo = IMESYNTAX;
ImErrorFatal( ImQError( ), -1, ImErrNo );
}
if (transparency==1)
{
if ( ImBinReadStruct( ioType, fd, fp, &block,
imGifControlBlockFields ) == -1 )
{
ImReturnBinError();
}
/* Put block.transparent_color in tagTable */
if (block.flags & IMGIFTRANSPARENCY)
{
ImMalloc(tmp, char * , 15 );
sprintf(tmp,"index=%d",(int)block.transparent_color);
TagTableAppend( tagTable,
TagEntryAlloc( "transparency value", POINTER, &tmp));
}
sprintf(message,"Pixels with index %d.",(int)block.transparent_color);
ImInfo ("Transparency",message);
}
else
{
/*
* Skip over extension data
*/
for ( ; ; )
{
/*
* Read in block size or block terminator
*/
if ( ImBinRead( ioType, fd, fp, &c, UCHAR, 1, 1 ) == -1 )
{
ImReturnBinError();
}
/*
* If block terminator, quit loop
*/
if ( c == IMGIFENDBLOCK )
break;
/*
* Skip over block
*/
if ( ImBinRead( ioType, fd, fp, buf, UCHAR, 1, c ) == -1 )
{
ImReturnBinError();
}
}
}
return( 0 );
}
/*
* FUNCTION
* imGifRead - read a gif image file
*
* DESCRIPTION
* The file header is read and the magic number checked.
* Separate routines are then called to handle different
* sizes (1 and anything from 2 to 8)
*/
static int /* Returns status */
#ifdef __STDC__
imGifRead( int ioType, int fd, FILE *fp,TagTable *flagsTable, TagTable *tagTable)
#else
imGifRead( ioType, fd, fp, flagsTable, tagTable )
int ioType; /* I/O flags */
int fd; /* Input file descriptor */
FILE *fp; /* Input file pointer */
TagTable *flagsTable; /* Flags */
TagTable *tagTable; /* Tag table to add to */
#endif
{
ImVfb *vfb; /* Read in image */
int nGClt,nLClt; /* Number of Global and Local */
/* CLT entries */
ImClt *gClt, *lClt; /* Read in colormaps */
unsigned char c; /* aux var */
int end; /* end to reading images */
int status; /* Return status */
int imageCount = 1;/* Number of images processed */
char message[100]; /* ImInfo message */
int nbits; /* # of bits per pixel */
/*
* Set the Binary I/O package's byte order
*/
BinByteOrder( BINLBF );
/*
* Read GIF header
*/
if ( ImBinReadStruct( ioType, fd, fp, &imGifHeader,
imGifHeaderFields )== -1)
{
ImReturnBinError( );
}
/*
* Check for valid Signature and version (treated as magic
* numbers). Always 6 bytes long.
*/
if( strncmp( (char*)imGifHeader.gif_magic, IMGIFMAGIC87a, 6 ) == 0 )
{
ImInfo( "Version", "GIF87a" );
}
else if ( strncmp( (char*)imGifHeader.gif_magic, IMGIFMAGIC89a, 6 ) == 0 )
{
ImInfo( "Version", "GIF89a" );
}
else
{
ImErrNo = IMEMAGIC;
ImErrorFatal( ImQError( ), -1, ImErrNo );
}
ImInfo( "Byte Order", "Least Significant Byte First" );
/*
* Read the Global Color Map if it exists
*/
gClt = IMCLTNULL;
lClt = IMCLTNULL;
if ( (imGifHeader.gif_flags & IMGIFGLOBALCM) == IMGIFGLOBALCM )
{
/*
* Compute the size of the GCLT and read it in.
*/
nGClt = 2 << ( imGifHeader.gif_flags & IMGIFBITPIXEL );
if ( imGifCltRead( ioType, fd, fp, nGClt, &gClt ) == -1 )
return ( -1 ); /* Error already handled */
sprintf( message, "%d Entries", nGClt );
ImInfo( "Global Color Table", message );
}
else
{
ImInfo( "Global Color Table", "none" );
}
/*
* Starts to read the images following
*/
end = 0;
while(!end)
{
if ( ImBinRead(ioType,fd,fp,&c,UCHAR,1,1 ) == -1 )
{
ImReturnBinError();
}
switch(c)
{
case IMGIFSEPARATOR: /* Image separator character */
/*
* Read the gif image descriptor
*/
if ( ImBinReadStruct( ioType, fd, fp, &imGifImDesc,
imGifImDescFields ) == -1 )
{
ImReturnBinError();
}
/*
* Output -verbose messages
*/
sprintf( message, "%d of ?", imageCount++ );
ImInfo( "Image", message );
sprintf( message, "%d x %d", imGifImDesc.im_width,
imGifImDesc.im_height );
ImInfo( "Resolution", message );
if ( (imGifImDesc.im_flags & IMGIFLOCALCM) == IMGIFLOCALCM)
{
nbits = (imGifImDesc.im_flags & IMGIFBITPIXEL) + 1;
sprintf( message, "%d-bit Color Indexed", nbits );
ImInfo( "Type", message );
}
else
{
/* No local color table. */
nbits = (imGifHeader.gif_flags&IMGIFBITPIXEL) + 1;
if ( gClt == NULL && nbits == 1 )
{
ImInfo( "Type", "1-bit Monochrome" );
}
else if ( gClt == NULL )
{
sprintf( message, "%d-bit Grayscale", nbits );
ImInfo( "Type", message );
}
else
{
sprintf( message, "%d-bit Color Indexed",nbits);
ImInfo( "Type", message );
}
}
/*
* Read the local color map if it exists
*/
if ( (imGifImDesc.im_flags & IMGIFLOCALCM) == IMGIFLOCALCM)
{
nLClt = 2 << ( imGifImDesc.im_flags & IMGIFBITPIXEL );
if ( imGifCltRead(ioType,fd,fp,nLClt,&lClt) == -1 )
return ( -1 );
sprintf( message, "%d Entries", nLClt );
ImInfo( "Local Color Table", message );
}
else
{
ImInfo( "Local Color Table", "none" );
}
ImInfo( "Compression Type", "Lempel-Ziv and Welch (LZW)");
/*
* Read the initial code size
*/
if(ImBinRead(ioType,fd,fp,&c, UCHAR, 1, 1 ) == -1 )
{
ImReturnBinError( );
}
/*
* Read the raster data into the vfb
*/
/*
* check the #bits/pixel
*/
if ( nbits == 1 )
status = imGifVfbRead1(ioType, fd, fp, flagsTable,
tagTable, &vfb,
(imGifImDesc.im_flags & IMGIFINTERLACED),c);
else
status = imGifVfbRead8( ioType, fd, fp, flagsTable,
tagTable, &vfb,
(imGifImDesc.im_flags & IMGIFINTERLACED),c);
if ( status == -1 )
return ( -1 ); /* Error already handled*/
/*
* Set the color lookup table
*/
/*
* if a local color map is present then it is assigned
* to the vfb. if not, then the global color table
* is used.
*/
if (((imGifImDesc.im_flags & IMGIFLOCALCM) == IMGIFLOCALCM )
&& ( lClt != IMCLTNULL) )
{
ImVfbSClt( vfb, lClt );
TagTableAppend( tagTable,
TagEntryAlloc( "image clt", POINTER, &lClt));
}
else if (gClt != IMCLTNULL)
{
ImVfbSClt( vfb, gClt );
TagTableAppend( tagTable,
TagEntryAlloc( "image clt", POINTER, &gClt));
}
TagTableAppend( tagTable,
TagEntryAlloc( "image vfb", POINTER, &vfb));
break;
case IMGIFEXTENSION: /* Gif extension block introducer */
if ( imGifExtRead(ioType,fd,fp, tagTable) == -1 )
return ( -1 ); /* Error already handled*/
break;
case IMGIFTERMINATOR: /* GIf terminator */
end = 1;
break;
}
}
return ( 1 );
}
/*
* FUNCTION
* imGifVfbRead8 - read 2-8 bit Gif image
*
* DESCRIPTION
* A new VFB is allocated. The image is read in, pixel by pixel,
* using the LZW algorithm, into the VFB. The order of the
* scanlines is given by the value of interlaced. If it is
* equal to IMGIFINTERLACED means that the scanlines are
* organized in an interlaced way. If not, they are written
* sequentially.
*/
static int /* Returns status */
#ifdef __STDC__
imGifVfbRead8(int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable, ImVfb **pVfb,
unsigned char interlaced, unsigned char codesize )
#else
imGifVfbRead8(ioType, fd, fp, flagsTable, tagTable, pVfb, interlaced, codesize )
int ioType; /* I/O flags */
int fd; /* Input file descriptor */
FILE *fp; /* Input file pointer */
TagTable *flagsTable; /* Flags */
TagTable *tagTable; /* Tag table to add to */
ImVfb **pVfb; /* VFB to fill and return */
unsigned char interlaced; /* tells if the image is inter */
unsigned char codesize; /* Inital code size used by LWZ */
/* laced */
#endif
{
unsigned char index; /* color index */
ImVfb *vfb; /* New vfb */
ImVfbPtr pptr; /* Pixel pointer */
int column; /* Column pixel into the image */
int row; /* Row pixel into the image */
int pass; /* used for interlaced image */
int value; /* pixel value returned by LWZ */
int size; /* size of image in pixels */
int cont; /* counter */
/*
* Allocate a VFB of the required size.
*/
if ( (*pVfb = ImVfbAlloc( imGifImDesc.im_width, imGifImDesc.im_height,
IMVFBINDEX8 )) == IMVFBNULL )
{
ImErrorFatal( ImQError( ), -1, ImErrNo );
}
vfb = *pVfb;
/* initializing lzw structures */
if ( imLzwReadByte(ioType, fd, fp, TRUE, codesize ) < 0 )
return( 0 );
size = imGifImDesc.im_width * imGifImDesc.im_height;
pptr = ImVfbQFirst( vfb );
cont = 0;
if (interlaced != IMGIFINTERLACED)
{
while ((cont < size) &&
((value = imLzwReadByte(ioType, fd, fp, FALSE, codesize)) >= 0) )
{
index = value;
ImVfbSIndex8( vfb, pptr, index);
ImVfbSInc( vfb, pptr );
cont++;
}
}
else
{
column = row = pass = 0;
while ((cont < size) &&
((value = imLzwReadByte(ioType, fd, fp, FALSE, codesize)) >= 0 ))
{
pptr = ImVfbQPtr( vfb, column,row );
index = value;
ImVfbSIndex8( vfb, pptr, index);
cont++;
if (++column == imGifImDesc.im_width)
{
column = 0;
switch (pass) {
case 0:
case 1:
row+= 8;
break;
case 2:
row+= 4;
break;
case 3:
row+= 2;
break;
}
if (row >= imGifImDesc.im_height )
switch(++pass)
{
case 1:
row = 4;
break;
case 2:
row = 2;
break;
case 3:
row = 1;
break;
}
}
}
}
return 1;
}
/*
* FUNCTION
* imGifVfbRead1 - read 1-bit Gif image
*
* DESCRIPTION
* A new VFB is allocated. The image is read in, one scanline at
* a time, and converted into the VFB.
*
*/
static int /* Returns status */
#ifdef __STDC__
imGifVfbRead1(int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable, ImVfb **pVfb,
unsigned char interlaced,unsigned char codesize )
#else
imGifVfbRead1(ioType, fd, fp, flagsTable, tagTable, pVfb, interlaced,codesize )
int ioType; /* I/O flags */
int fd; /* Input file descriptor */
FILE *fp; /* Input file pointer */
TagTable *flagsTable; /* Flags */
TagTable *tagTable; /* Tag table to add to */
ImVfb **pVfb; /* VFB to fill and return */
unsigned char interlaced; /* tells if the image is inter */
unsigned char codesize; /* Initial code size used by LWZ*/
/* laced */
#endif
{
ImVfb *vfb; /* New vfb */
ImVfbPtr pptr; /* Pixel pointer */
int column; /* Column pixel into the image */
int row; /* Row pixel into the image */
int pass; /* used for interlaced image */
int value; /* pixel value returned by LWZ */
int size; /* size of image in pixels */
int cont;
/*
* Allocate a VFB of the required size.
*/
if ( (*pVfb = ImVfbAlloc( imGifImDesc.im_width, imGifImDesc.im_height,
IMVFBMONO )) == IMVFBNULL )
{
ImErrorFatal( ImQError( ), -1, ImErrNo );
}
vfb = *pVfb;
/* initializing lzw structures */
if ( imLzwReadByte(ioType, fd, fp, TRUE, (int) codesize ) < 0 )
return( 0 );
size = imGifImDesc.im_width * imGifImDesc.im_height;
pptr = ImVfbQFirst( vfb );
cont = 0;
if (interlaced != IMGIFINTERLACED)
{
while ((cont < size) &&
((value = imLzwReadByte(ioType, fd, fp, FALSE, codesize)) >= 0) )
{
ImVfbSMono( vfb, pptr, (value) ? 1 : 0);
ImVfbSInc( vfb, pptr );
cont++;
}
}
else
{
column = row = pass = 0;
while ((cont < size) &&
((value = imLzwReadByte(ioType, fd, fp, FALSE, codesize)) >= 0))
{
pptr = ImVfbQPtr( vfb, column,row );
ImVfbSMono ( vfb, pptr, (value) ? 1 : 0);
cont++;
if (++column == imGifImDesc.im_width)
{
column = 0;
switch (pass) {
case 0:
case 1:
row+= 8;
break;
case 2:
row+= 4;
break;
case 3:
row+= 2;
break;
}
if (row >= imGifImDesc.im_height )
switch(++pass)
{
case 1:
row = 4;
break;
case 2:
row = 2;
break;
case 3:
row = 1;
break;
}
}
}
}
return 1;
}
/*
* FUNCTION
* imGifCltWrite - Write a color lookup table
*
* DESCRIPTION
* Write the color lockup table clt into the file
*/
static int /* Returns status */
#ifdef __STDC__
imGifCltWrite(int ioType, int fd, FILE *fp, int nClt,int newClt, ImClt *clt)
#else
imGifCltWrite(ioType, fd, fp, nClt,newClt, clt)
int ioType;
int fd;
FILE *fp;
int nClt;
int newClt;
ImClt *clt;
#endif
{
ImCltPtr cptr; /* Color pointer */
unsigned char *cltBuffer; /* Color lookup table buffer */
unsigned char *colorp; /* Color buffer pointer */
int i;
ImMalloc( cltBuffer, unsigned char *, sizeof( unsigned char ) * newClt * 3 );
colorp = cltBuffer;
cptr = ImCltQFirst( clt );
for (i=0; i< nClt; i++)
{
*(colorp++) = ImCltQRed ( cptr );
*(colorp++) = ImCltQGreen( cptr );
*(colorp++) = ImCltQBlue ( cptr );
ImCltSInc( clt, cptr );
}
if ( ImBinWrite( ioType, fd, fp, cltBuffer, UCHAR, 1, newClt * 3 ) == -1)
{
free( (unsigned char *)cltBuffer );
ImReturnBinError( );
}
return 1;
}
/*
* FUNCTION
* imGifVfbWrite8 - Write an 8-bit vfb into a gif file
*
* DESCRIPTION
*/
static int /* Returns status */
#ifdef __STDC__
imGifVfbWrite8( int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable, ImVfb *vfb,
unsigned char interlaced )
#else
imGifVfbWrite8( ioType, fd, fp, flagsTable, tagTable, vfb, interlaced )
int ioType; /* I/O flags */
int fd; /* Input file descriptor */
FILE *fp; /* Input file pointer */
TagTable *flagsTable; /* Flags */
TagTable *tagTable; /* Tag table to add to */
ImVfb *vfb; /* VFB to write */
unsigned char interlaced; /* tells if the image is inter */
/* laced */
#endif
{
unsigned char *rasterimage; /* image pixel values */
int i;
int size; /* size of the image */
int pass;
int row,column;
ImVfbPtr ptr;
unsigned char c;
/*
* Write an Image separator
*/
c = IMGIFSEPARATOR;
if ( ImBinWrite( ioType, fd, fp, &c, UCHAR, 1, 1 ) == -1 )
{
ImReturnBinError( );
}
/*
* Setup the Image Descriptor and write it out
*/
imGifImDesc.im_left = imGifImDesc.im_top = 0;
imGifImDesc.im_width = ImVfbQWidth ( vfb );
imGifImDesc.im_height = ImVfbQHeight ( vfb );
imGifImDesc.im_flags = interlaced;
if ( ImBinWriteStruct( ioType, fd, fp, &imGifImDesc,
imGifImDescFields ) == -1 )
{
ImReturnBinError( );
}
/*
* Write out the initial code size before
* start to compress the raster data
*/
c = IMGIFCODESIZE;
if ( ImBinWrite( ioType, fd, fp, &c, UCHAR, 1, 1 ) == -1 )
{
ImReturnBinError( );
}
/*
* Allocate a buffer to store the image pixel values
* to be compressed
*/
size = imGifImDesc.im_width * imGifImDesc.im_height;
ImMalloc( rasterimage, unsigned char *, size );
if ( rasterimage == NULL )
{
ImErrNo = IMEMALLOC;
ImErrorFatal( ImQError( ), -1, ImErrNo );
}
/*
* Fill the buffer with the pixel values
*/
ptr = ImVfbQFirst(vfb);
if (interlaced != IMGIFINTERLACED)
for (i=0; i< size; i++)
{
rasterimage[i] = ImVfbQIndex8(vfb,ptr);
ImVfbSInc(vfb,ptr);
}
else
{
column = row = pass = 0;
for (i=0; i< size; i++)
{
ptr = ImVfbQPtr( vfb, column,row );
rasterimage[i] = ImVfbQIndex8(vfb,ptr);
if (++column == imGifImDesc.im_width)
{
column = 0;
switch (pass) {
case 0:
case 1:
row+= 8;
break;
case 2:
row+= 4;
break;
case 3:
row+= 2;
break;
}
if (row >= imGifImDesc.im_height )
switch(++pass)
{
case 1:
row = 4;
break;
case 2:
row = 2;
break;
case 3:
row = 1;
break;
}
}
}
}
/*
* Compress the raster data
*/
imLzwCompGif( ioType, fd, fp, IMGIFCODESIZE + 1, rasterimage, size);
free ((unsigned char *) rasterimage);
/*
* Write out a Zero-length packet
*/
c = 0;
if ( ImBinWrite( ioType, fd, fp, &c, UCHAR, 1, 1 ) == -1 )
{
ImReturnBinError( );
}
/*
* Write the Gif terminator
*/
c = IMGIFTERMINATOR;
if ( ImBinWrite( ioType, fd, fp, &c, UCHAR, 1, 1 ) == -1 )
{
ImReturnBinError( );
}
return(1);
}
/*
* FUNCTION
* imGifVfbWrite1 - Write an 1-bit vfb into a gif file
*
*/
static int /* Returns status */
#ifdef __STDC__
imGifVfbWrite1( int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable, ImVfb *vfb,
unsigned char interlaced )
#else
imGifVfbWrite1( ioType, fd, fp, flagsTable, tagTable, vfb, interlaced )
int ioType; /* I/O flags */
int fd; /* Input file descriptor */
FILE *fp; /* Input file pointer */
TagTable *flagsTable; /* Flags */
TagTable *tagTable; /* Tag table to add to */
ImVfb *vfb; /* VFB to write */
unsigned char interlaced; /* tells if the image is inter */
/* laced */
#endif
{
unsigned char *rasterimage; /* image pixel values */
int i; /* counter */
int size; /* size of the image */
int pass; /* pass number */
int row,column; /* pixel row and pixel column */
ImVfbPtr ptr; /* pixel pointer */
unsigned char c; /* auxiliar var */
/*
* Write an Image separator
*/
c = IMGIFSEPARATOR;
if ( ImBinWrite( ioType, fd, fp, &c, UCHAR, 1, 1 ) == -1 )
{
ImReturnBinError( );
}
/*
* Setup the Image Descriptor and write it out
*/
imGifImDesc.im_left = imGifImDesc.im_top = 0;
imGifImDesc.im_width = ImVfbQWidth ( vfb );
imGifImDesc.im_height = ImVfbQHeight ( vfb );
imGifImDesc.im_flags = interlaced;
if ( ImBinWriteStruct( ioType, fd, fp, &imGifImDesc,
imGifImDescFields ) == -1 )
{
ImReturnBinError( );
}
/*
* Write out the initial code size before
* start to compress the raster data. When
* the number of bits per pixel is 1 the
* initial code size used for LWZ algorithm is
* equal to 2.
*/
c = 2;
if ( ImBinWrite( ioType, fd, fp, &c, UCHAR, 1, 1 ) == -1 )
{
ImReturnBinError( );
}
/*
* Allocate a buffer to store the image pixel values
* to be compressed
*/
size = imGifImDesc.im_width * imGifImDesc.im_height;
ImMalloc( rasterimage, unsigned char *, size );
if ( rasterimage == NULL )
{
ImErrNo = IMEMALLOC;
ImErrorFatal( ImQError( ), -1, ImErrNo );
}
/*
* Fill the buffer with the pixel values
*/
ptr = ImVfbQFirst(vfb);
if (interlaced != IMGIFINTERLACED)
for (i=0; i< size; i++)
{
rasterimage[i] = (ImVfbQMono(vfb,ptr)? 1 : 0);
ImVfbSInc(vfb,ptr);
}
else
{
column = row = pass = 0;
for (i=0; i< size; i++)
{
ptr = ImVfbQPtr( vfb, column,row );
rasterimage[i] = (ImVfbQMono(vfb,ptr)? 1 : 0);
if (++column == imGifImDesc.im_width)
{
column = 0;
switch (pass) {
case 0:
case 1:
row+= 8;
break;
case 2:
row+= 4;
break;
case 3:
row+= 2;
break;
}
if (row >= imGifImDesc.im_height )
switch(++pass)
{
case 1:
row = 4;
break;
case 2:
row = 2;
break;
case 3:
row = 1;
break;
}
}
}
}
/*
* Compress the raster data
*/
/*
* 3 = initial code size + 1
* the initial code size is always 2 for
* one-bit images
*/
imLzwCompGif( ioType, fd, fp, 3, rasterimage, size);
free ((unsigned char *) rasterimage);
/*
* Write out a Zero-length packet
*/
c = 0;
if ( ImBinWrite( ioType, fd, fp, &c, UCHAR, 1, 1 ) == -1 )
{
ImReturnBinError( );
}
/*
* Write the Gif terminator
*/
c = IMGIFTERMINATOR;
if ( ImBinWrite( ioType, fd, fp, &c, UCHAR, 1, 1 ) == -1 )
{
ImReturnBinError( );
}
return(1);
}
/*
* FUNCTION
* imGifWrite - write a vfb image into gif image format
*
* DESCRIPTION
*/
static int /* Returns # of tags written */
#ifdef __STDC__
imGifWrite( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable )
#else
imGifWrite( pMap, ioType, fd, fp, flagsTable, tagTable )
ImFileFormatWriteMap *pMap; /* Write map entry to adhear to */
int ioType; /* I/O flags */
int fd; /* Output file descriptor */
FILE *fp; /* Output file pointer */
TagTable *flagsTable; /* Format Flags */
TagTable *tagTable; /* Tag list to add to */
#endif
{
ImVfb *vfb; /* Read in image */
int nClt; /* Number of CLT entries */
ImClt *clt; /* Read in colormap */
int newClt; /* Number of CLT entries rounded to a power of two */
int nbitspixel; /* number of bits per pixel */
char message[100]; /* ImInfo message */
int transparency; /* transparency value */
ImInfo( "Version", "GIF89a" );
ImInfo( "Byte Order", "Least Significant Byte First" );
TagEntryQValue( TagTableQDirect( tagTable, "image vfb", 0 ), &vfb );
/*
* Set up the header and write it out
*/
strncpy((char*)imGifHeader.gif_magic,IMGIFMAGIC89a,6);
imGifHeader.gif_width = ImVfbQWidth ( vfb );
imGifHeader.gif_height = ImVfbQHeight( vfb );
/*
* Decide how to write out the GIF file.
*/
if (pMap->map_outAttributes & IMCLTYES)
{
/*
* A CLT should be written out. Higher level code will
* have insured that it has a CLT. Figure out how many
* bits per pixel based upon the CLT size.
*/
clt = ImVfbQClt( vfb );
nClt= ImCltQNColors( clt );
nbitspixel = imGifNBitsPixel(nClt,&newClt);
imGifHeader.gif_flags = IMGIFGLOBALCM;
imGifHeader.gif_flags|= (IMGIFNCOLORRES - 1) << 4;
}
else
{
/*
* No CLT to write out. Could be grayscale or monochrome.
*/
if ( ImVfbQFields( vfb ) & IMVFBINDEX8 )
nbitspixel = 8;
else
nbitspixel = 1;
imGifHeader.gif_flags = IMGIFNOCOLORMAP;
}
imGifHeader.gif_flags |= (nbitspixel-1);
imGifHeader.gif_background = IMGIFBACKGROUND;
imGifHeader.gif_aspect =
( ImVfbQWidth( vfb ) / ImVfbQHeight( vfb ) + 15 ) / 64;
BinByteOrder( BINLBF );
if ( ImBinWriteStruct( ioType, fd, fp, &imGifHeader,
imGifHeaderFields ) == -1 )
{
ImReturnBinError( );
}
/*
* Write the CLT if exists
*/
if (pMap->map_outAttributes & IMCLTYES)
{
if ( imGifCltWrite(ioType, fd, fp, nClt, newClt, clt) == -1 )
return ( -1 );
sprintf( message, "%d Entries", nClt );
ImInfo( "Global Color Table", message );
}
else
ImInfo( "Global Color Table", "none" );
ImInfo( "Image", "1 of 1" );
sprintf( message, "%d x %d", imGifHeader.gif_width,
imGifHeader.gif_height );
ImInfo( "Resolution", message );
/*
* If there is a request for transparency or if
* there's transparency in the tagTable, add it to
* the image.
*/
if ((transparency=ImGetTransparency(tagTable, flagsTable, vfb))!=-1)
{
if (imGifWriteTransparency( ioType, fd, fp, transparency)==-1)
{
return -1;
}
}
/* query the vfb and call the appropiate routine */
if ((ImVfbQFields( vfb ) & IMVFBMONO ))
{
ImInfo( "Type", "1-bit Monochrome" );
ImInfo( "Local Color Table", "none" );
ImInfo( "Compression Type", "Lempel-Ziv and Welch (LZW)" );
return(imGifVfbWrite1(ioType,fd,fp,flagsTable,tagTable,
vfb,IMGIFINTERLACED));
}
else
{
if ( pMap->map_outAttributes & IMCLTYES )
sprintf( message, "%d-bit Color Indexed", nbitspixel );
else
sprintf( message, "%d-bit Grayscale", nbitspixel );
ImInfo( "Type", message );
ImInfo( "Local Color Table", "none" );
ImInfo( "Compression Type", "Lempel-Ziv and Welch (LZW)" );
return(imGifVfbWrite8(ioType,fd,fp,flagsTable,tagTable,
vfb,IMGIFINTERLACED));
}
}
/*
* FUNCTION
* imGifNBitsPixel - compute # of bits to represent a color
*
* DESCRIPTION
* Rounds the number of colors to the closest power
* of two and return the necesary number of bits to
* represent this number of colors.
*/
static int /* Returns # of bits */
#ifdef __STDC__
imGifNBitsPixel (int ncolors,int *newNumber)
#else
imGifNBitsPixel (ncolors,newNumber)
int ncolors; /* number of colors */
int *newNumber; /* new number of colors */
#endif
{
int nbits;
*newNumber = 2;
nbits = 1;
if ( ncolors > 256)
{
nbits = 8;
*newNumber = 256;
}
else
{
while (*newNumber < ncolors)
{
(*newNumber) <<= 1;
nbits++;
}
}
return(nbits);
}
/*
* FUNCTION
* imGifWriteTransparency
*
* DESCRIPTION
* Write out the color which is transparent in the image
* as specified by the flags table. (As a default, write out
* the most popular color in the image.)
*
* If the tagEntry parameter specifies an index value, use that.
*
* If the tagEntry parameter specifes an rgb value, use the most
* common index value with that rgb value.
*
* If the tagEntry parameter speciefies "most common", use the
* most common index value in the image.
*
*
*/
static int /* returns status */
#ifdef __STDC__
imGifWriteTransparency( int ioType, int fd, FILE* fp, int transparency)
#else
imGifWriteTransparency( ioType, fd, fp, transparency)
int ioType;
int fd;
FILE* fp;
int transparency;
#endif
{
imGifControlBlock controlBlock; /* data */
unsigned char blockId; /* id for block */
char message[1000]; /*message buffer */
/* Specify that this is an extension */
blockId = IMGIFEXTENSION;
if ( ImBinWrite( ioType, fd, fp, &blockId, UCHAR, 1, 1 ) == -1 )
{
ImReturnBinError( );
}
/* Specify what type of extension this is */
blockId = IMGIFGRAPHICCONTROL;
if ( ImBinWrite( ioType, fd, fp, &blockId, UCHAR, 1, 1 ) == -1 )
{
ImReturnBinError( );
}
sprintf(message,"Pixels with index %d.",transparency);
ImInfo("Transparency",message);
controlBlock.blocksize = 0x04;
controlBlock.flags = 0x01;
controlBlock.delay = 0x00;
controlBlock.transparent_color = (unsigned char)transparency;
controlBlock.terminator = 0x00;
if ( ImBinWriteStruct( ioType, fd, fp, &controlBlock,
imGifControlBlockFields ) == -1 )
{
ImReturnBinError( );
}
return 1;
}