Subversion Repositories wimsdev

Compare Revisions

Ignore whitespace Rev 9 → Rev 10

/trunk/wims/src/Misc/whirlgif/CHANGES
0,0 → 1,51
Rev 3.04 21Feb99 Hans Dinsen-Hansen
Drastic changes of the original GIF decoder. The code can now
read LZW as well as RunLength encoded data from e.g. GNU plot.
Default disposal changed. Changes in forward declarations
inspired by the Amiga people.
 
Rev 3.03 02Feb99 Hans Dinsen-Hansen
Patched misleading error messages from the decoder when minimizing
GIFs from GNU plot.
 
Rev 3.02 01Nov98 Hans Dinsen-Hansen
Fixed an error in the loop option. Further minimizing of outfile.
Search for nearest color bottom to top, to be conformant with most
paint programs. Modifications of Makefile to be more conformant
with the requirements in news:comp.sources.unix
 
Rev 3.01 Sep/Oct98 Hans Dinsen-Hansen
Never published. Modifications for dealing with microSoft
problems. Filenames changed to the infamous 8+3 DOS format for
what used to be gif{en,de}code.c.
 
Rev 3.00 29Jul98 Hans Dinsen-Hansen
Included re-encoding of GIFs, permitting minimizing of the
resulting GIF, by outputting only the smallest differing
rectangle between two GIFs. Option for forcing the first
color map to be globally used, by choosing the nearest color,
if a local color does not exist in the global map.
 
Rev 2.02 09Sep97 Hans Dinsen-Hansen
Pernmitted GIF89a input; do not use the local colormap
if it is equal to the global; option for background index;
included the comment block within the GIF & option for a
user defined commentblock; fixed the disposal option.
 
Rev 2.01 31Aug96 Kevin Kadow
Added 'disp' options to set image disposal, fixed errors in
comment block and with the Netscape loop extension.
 
Rev 2.00 05Feb96 Kevin Kadow
Added transparency and gif comments.
 
Rev 1.10 29Jan96 Kevin Kadow
First release of whirlgif.
 
txtmerge:
 
Rev 1.01 08Jan92 Mark Podlipec
Use all colormaps, not just 1st.
 
Rev 1.00 23Jul91 Mark Podlipec
creation
/trunk/wims/src/Misc/whirlgif/whirlgif.1
0,0 → 1,316
.\" whirlgif.1
.\"
.\" The Graphics Interchange Format (c) is the Copyright property of
.\" Compuserve Incorporated. GIF(sm) is a Service Mark property of
.\" Compuserve Incorporated.
.\"
.TH WHIRLGIF 1 "February 1999"
.AT 3
.SH NAME
whirlgif \- reads a series of GIF87a or
GIF89a files, and produces
one single GIF89a file composed of those images.
.SH SYNOPSIS
.B whirlgif
.RI [ "global-options" ]
.RI [ "file-options " { GIFfile "|\fB-i\fP " incfile "}] ..."
.sp
The
.I global-options
are one or more of:
.br
.in +5
.B \-v
.br
.BI \-trans " index"
.br
.BR \-background " {\fIcolor\fP|\fIindex\fP}"
.br
.B \-time
.I delay
.br
.B \-globmap
.br
.B \-minimize
.br
.B \-o
.I outfile
.br
.B \-loop
.RI [ count ]
.br
.B \-comment
.I comment
.br
.B \-disp
.I method
.in -5
.sp
The
.I file-options
are one or more of:
.br
.in +5
.B \-off
.I x:y-pair
.br
.BR \-trans " {\fIcolor\fP|\fIindex\fP}"
.br
.B \-time
.I delay
.br
.B \-disp
.I method
.sp
.SH DESCRIPTION
When
.I Whirlgif
processes a series of GIF files,
the first file defines the so called screen size and the background
color of the resulting GIF. The background color may be changed by an
option.
.sp
The input files may be in either GIF87a or GIF89a format. If a GIF
file contains more than one image, only the first image is read. To
help saving bandwidth on the internet, there are options for forcing
all GIFs in the series to use the same color map and to create
sub-GIFs which only give the difference between one image and the next.
.PP
.SH OPTIONS
.PP
.TP
.B \-v
Verbose mode. Output is written to the error out file.
This is a global option and must be given before any
.IR GIFfile s.
.TP
\fB\-trans\fP \fIindex\fP|\fIcolor\fP
Set the color identified with
.I index
or the color code
.BI # xxxxxx \fR,\fP
where
.I xxxxxx
is a hex
.B RGB
index to be transparent.
When this option is used as a
.IR file-option ,
its setting is valid for all subsequent
.IR GIFfile s,
until it is used again.
 
.TP
\fB\-background\fP index
Set the color identified with its
.I index
to be the background color, possibly
overriding the background color defined in the first
.IR GIFfile .
This is a global option and must be given before any
.IR GIFfile s.
.TP
\fB\-time\fP \fIdelay\fP
Defines inter\-frame timing in units of 1/100 second. Whenever a value of
\fIdelay\fP is set by means of the \fB\-time\fP option, this value will
be valid for the following
.IR GIFfile s
until a new value is set.
.TP
\fB\-globmap\fP
Use the color map of the first
.I GIFfile
as the global color map throughout
the
.IR outfile .
If the color maps of subsequent
.IR GIFfile s
contain
colors that are not in the global color map, the nearest colors in a
3 dimensional RGB space will be chosen.
This is a global option and must be given before any
.IR GIFfile s.
.TP
\fB\-minimize\fP
Only the smallest rectangles covering the difference between two
.IR GIFfile s
will be output. All
.IR GIFfile s
must be of the same size and have the same offset.
This is tested, and may result in an error message.
This is a global option and must be given before any
.IR GIFfile s.
.sp
As a side effect, the \fB\-minimize\fP
option forces the same (global)color map to be used throughout the
.IR outfile .
.TP
\fB\-o\fP outfile
Write the results to
.I outfile
This is a global option and must be given before any
.IR GIFfile s.
.TP
\fB\-loop\fP [\fIcount\fP]
Add the Netscape 'loop' extension. If count is omitted, 0 (zero) is
assumed.
This is a global option and must be given before any
.IR GIFfile s.
.TP
.B "\-comment \fIcomment\fP"
Define a \fBComment Extension\fP block
within the resulting GIF file.
This block will be written at the end of the file.
A comment block can be at most 254 characters long, including line
shifts.
It may be used for copyright notices.
This is a global option and must be given before any
.IR GIFfile s.
.TP
\fB\-disp\fP \fImethod\fP
Sets the image 'disposal' method. When this option is used as a
.IR file-option ,
its setting is valid for all subsequent
.IR GIFfile s,
until it is used again. The methods may be one of
the keywords:
.sp
.in +5
.ti -5
.B none
No disposal specified.
.sp
.ti -5
.B back
Restore to background color.
Fill the image's space with the background color.
.sp
.ti -5
.B prev
Restore to previous, restores the screen area
to what was there before. Netscape has a problem with this option.
.sp
.ti -5
.B not
Do not dispose. The default, the next image will overlay this one.
.sp
.in -5
.TP
\fB\-i\fP incfile
Read a list of names from
.I incfile
.TP
.B "\-off \fIx:y-pair\fP"
Sets the position of the next
.I GIFfile
or the set of
.IR GIFfile s
in an
.IR incfile .
.sp
The \fIx:y-pair\fP consists of two integers, separated by one
character which cannot be part of a number. A colon, ':' is preferred.
The two integers define the position of the upper left corner of the
next image within the 'screen' of the resulting GIF.
The integers are added to a possible offset within the next
.IR GIFfile .
One or both may be negative.
Negative numbers will re-position an image upwards
and/or to the left within its own 'screen'.
The 'screen' of a GIF is the term used in the GIF definitions for the
area, a GIF covers.
.SH TIPS
.PP
If you don't specify an output file, the GIF will be sent to stdout.
This is a good thing if you're using whirlgif in a CGI script,
but could cause problems if you run from a terminal and forget to
redirect stdout.
.sp
The output file (if any) and the loop option
.B MUST
be specified before any GIF images.
.sp
You can specify several delay statements on the command line to change
the delay between images in the middle of an animation, e.g.
.sp
.ti -5
whirlgif \-time 25 a.gif b.gif c.gif \-time 100 d.gif \-time 25 e.gif f.gif
.sp
Usually, the bacground color is defined by the first
.IR GIFfile ,
and often it is index number zero (0).
If you are animating with a transparent color,
a good method is usually to use the same color as the background.
.sp
It is possible, but may cause problems for some programs, to specify
several transparency statements on the command line.
.sp
The size of the resulting GIF and the global color map is
defined by the first
.IR GIFfile .
If you have a large background GIF and have several smaller GIFs, you
may make some kind of a bulletin board by a command of the following form:
.sp
.ti -5
whirlgif large.gif -off 77:44 small1.gif -off 22:99 small2.giff ...
.sp
You may build in some timing between the small GIFs.
.SH LIMITATIONS
.PP
The loop 'count' is ineffective because Netspcape always loops infinitely.
.SH BUGS
.PP
.I Whirlgif
should be able to specify delay and offset in an 'incfile' list (see next bug).
.sp
Does not handle filenames starting with a \- (hypen), except in 'incfile'.
.SH TODO
.PP
.ti +2
\- Make a graphic interface. Perhaps by re-implementing the
algorithms in Java(tm).
.sp
.ti +2
\- option for other choices of unification methods for colormaps.
The nearest point in the 3 dimensional RGB color space may not be
the best choice.
.sp
.ti +2
\- define extension block in order to have frame lists.
.sp
.ti +2
\- define single-letter options and use
.IR getopt (3)
like most UNIX programs.
.SH AVAILABILITY
.PP
This program is available via the
archive for comp.soruces.unix
.B http://sources.isc.org/
(and also via http://www.danbbs.dk/~dino/whirlgif/ ).
.SH AUTHORs
.PP
V3.00 \- 3.04 Hans Dinsen-Hansen <dino@danbbs.dk>
.br
Based on
Whirlgif V2.02 by Kevin Kadow <kadokev@msg.net>
.br
.ti +5
& Hans Dinsen-Hansen <dino@danbbs.dk>
.br
which is again based on 'txtmerge' written by:
.br
Mark Podlipec <podlipec@BayNetworks.com>
.br
Man Page by Mintak Ng <mintak@hitak.com>
.br
.ti +5
& Hans Dinsen-Hansen <dino@danbbs.dk>
.sp
.SH COPYRIGHT NOTICES
.PP
The Graphics Interchange Format (c) is the Copyright property of
Compuserve Incorporated. GIF(sm) is a Service Mark property of
Compuserve Incorporated.
.sp
See other Copyright notices in the accompanying texts.
/trunk/wims/src/Misc/whirlgif/whirlgif.c
0,0 → 1,751
/*
* whirlgif.c
*
* Copyright (c) 1997,1998,1999 by Hans Dinsen-Hansen (dino@danbbs.dk)
* Copyright (c) 1995,1996 by Kevin Kadow (kadokev@msg.net)
* Based on txtmerge.c
* Copyright (c) 1990,1991,1992,1993 by Mark Podlipec (podlipec@BayNetworks.com).
* All rights reserved.
*
* This software may be freely copied, modified and redistributed
* without fee provided that above copyright notices are preserved
* intact on all copies and modified copies.
*
* There is no warranty or other guarantee of fitness of this software.
* It is provided solely "as is". The author(s) disclaim(s) all
* responsibility and liability with respect to this software's usage
* or its effect upon hardware or computer systems.
*
* The Graphics Interchange format (c) is the Copyright property of
* Compuserve Incorporated. Gif(sm) is a Service Mark property of
* Compuserve Incorporated.
*
*/
/*
* Description:
*
* This program reads in a sequence of single-image Gif format files and
* outputs a single multi-image Gif file, suitable for use as an animation.
*
* TODO:
*
* More options for dealing with the colormap
*
*/
 
/*
* Rev 3.04 21Feb99 Hans Dinsen-Hansen
* RunLength & Amiga.
* Rev 3.03 02Feb99 Hans Dinsen-Hansen
* Published as a patch. Better error messages.
* Rev 3.02 01Oct98 Hans Dinsen-Hansen
* Loop. Verbose -> DEBUG. Further minimizing.
* Rev 3.01 Oct98 Hans Dinsen-Hansen
* Never published. Various experiments with Windows versions.
* Rev 3.00 29jul98 Hans Dinsen-Hansen
* Gif-repacking; unification of color map; only output diff.
* Rev 2.02 09Sep97 Hans Dinsen-Hansen
* Gif89a input; use global colormap whenever possible; background index
* Rev 2.01 31Aug96 Kevin Kadow
* disposal
* Rev 2.00 05Feb96 Kevin Kadow
* transparency, gif comments,
* Rev 1.10 29Jan96 Kevin Kadow
* first release of whirlgif
*
* txtmerge:
* Rev 1.01 08Jan92 Mark Podlipec
* use all colormaps, not just 1st.
* Rev 1.00 23Jul91 Mark Podlipec
* creation
*
*
*/
 
#include "whirlgif.h"
 
/*
* Set some defaults, these can be changed on the command line
*/
unsigned int loop=DEFAULT_LOOP, loopcount=0,
useColormap=DEFAULT_USE_COLORMAP, debugFlag=0,
globmap=0, minimize=0;
 
int imagex = 0, imagey = 0, imagec = 0, GifBgcolor=0, count=0;
 
/* global settings for offset, transparency */
 
Global global;
 
GifColor gifGmap[256], gifCmap[256];
GifScreenHdr globscrn, gifscrn;
 
GifImageHdr gifimage, gifimageold;
 
extern ULONG gifMask[];
extern int picI;
UBYTE *pixold=NULL;
ULONG gifMask[16]={0,1,3,7,15,31,63,127,255,511,1023,2047,4095,8191,0,0}, obits;
ULONG gifPtwo[16]={1,2,4,8,16,32,64,128,256,512,1024,2048,4096,8192,0,0};
 
char gifFileName[BIGSTRING];
FILE *ff;
 
long sq(UBYTE i,UBYTE j)
{
return((i-j)*(i-j));
}
 
void main(argc, argv)
int argc;
char *argv[];
{
FILE * infile, *fout;
char temp[BIGSTRING], *cmt;
int i;
 
fprintf(stderr, "whirlgif Rev %2.2f (c) 1997-1999 by %s\n%s\n%s\n",
DA_REV,"Hans Dinsen-Hansen",
" (c) 1995-1996 by Kevin Kadow",
" (c) 1990-1993 by Mark Podlipec");
cmt = temp;
/* if there is no comment option, let cmt point at the final message */
 
if (argc < 2) Usage();
 
/* set global values */
global.trans.type = TRANS_NONE;
global.trans.valid = 0;
global.time = DEFAULT_TIME;
global.left = 0;
global.top = 0;
global.disposal = DEFAULT_DISPOSAL;
 
fout = stdout;
i = 1;
while( i < argc) {
char *p;
p = argv[i];
if (debugFlag > 1) fprintf(stderr, "Option: %s\n", p);
if ( (p[0] == '-') || (p[0] == '+') ) {
++p; /* strip off the - */
switch(p[0]) {
case 'v': /* Give lots of information */
debugFlag++;
i++;
fprintf(stderr, "Verbose output; debug level: %d\n", debugFlag);
break;
case 'g': /* Use the global colormap throughout */
globmap++;
i++;
if ( debugFlag > 1) fprintf(stderr, "globmap\n");
break;
case 'm': /* minimize output */
minimize++;
globmap++;
i++;
if ( debugFlag > 1) fprintf(stderr, "minimize \n");
break;
case 'd': /* disposal setting */
i++;
p = argv[i++];
if(!strcmp("not", p)) global.disposal = DISP_NOT;
else if(!strcmp("back", p)) global.disposal = DISP_BACK;
else if(!strcmp("prev", p)) global.disposal = DISP_PREV;
else if(!strcmp("none", p)) global.disposal = DISP_NONE;
else global.disposal = DEFAULT_DISPOSAL;
if(debugFlag) fprintf(stderr, "Disposal method set to %s = %d\n",
p, global.disposal);
break;
case 'D': /* Debug setting */
i++;
debugFlag = 2;
fprintf(stderr, "DEBUG: Debug Level %d\n", debugFlag);
break;
case 'c': /* set comment pointer */
i++;
cmt = argv[i++];
if(debugFlag) fprintf(stderr, "Comment: '%s'\n", cmt);
break;
case 'b': /* set Background color index */
i++;
GifBgcolor = atoi(argv[i++]) | 0x100;
if (debugFlag) fprintf(stderr, "BACKGROUND = %d\n", GifBgcolor & 0xff);
break;
case 'l': /* Enable looping */
loop = TRUE;
i++;
if(*argv[i] != '-') {
/* a loop count was perhaps given */
loopcount = atoi(argv[i]);
if(debugFlag) {
fprintf(stderr, loopcount != 0 ? "Loop %d times\n"
: "Loop forever, count = %d\n", loopcount);
}
if( (loopcount > 0) | ((loopcount == 0) & (*argv[i] == '0'))) i++;
}
else {
/* default to infinite loop */
loopcount = 0;
if(debugFlag) fprintf(stderr, "Looping enabled\n");
}
break;
case 't': /* either time or transparent */
i++;
if(!strncmp("time", p, 2)) {
/* Delay time in 1/100's of a second */
global.time = atoi(argv[i++]);
}
else if(!strncmp("trans", p, 2)) CalcTrans(argv[i++]);
break;
case 'o': /* Output file - send output to a given filename */
i++;
if(!strncmp("off", p, 2)) SetOffset(argv[i]);
else if(NULL == (fout = fopen(argv[i], WRIBIN))) {
/* It must be 'output, so we do that */
fprintf(stderr, "Cannot open %s for output\n", argv[i]);
exit(1);
}
i++;
break;
case 'i': /* input file - file with a list of images */
i++;
if(NULL != (infile = fopen(argv[i], REATXT))) {
while(fgets(gifFileName, BIGSTRING, infile)) {
strtok(gifFileName, "\n");
GifReadFile(fout, gifFileName, count++ == 0);
}
fclose(infile);
global.left = global.top = 0;
}
else fprintf(stderr, "Cannot read list file %s\n", argv[i]);
i++;
break;
default:
Usage();
exit(0);
break;
}
continue;
}
/* Not an option, must be the name of an input file */
GifReadFile(fout, argv[i], count++ == 0);
global.left = global.top = 0;
i++;
}
/* We're done with all options and file names, finish up */
if(count >0) {
sprintf(temp, "whirlgif %2.2f (c) %s\r\n%d %s",
DA_REV, "dino@danbbs.dk", count,
count == 1 ? "image" : "images");
/* Either output above std. mess. or a possible user defined comment */
GifComment(fout, cmt);
}
fputc(';', fout); /* End of Gif file */
 
fclose(fout);
fprintf(stderr, "Processed %d files.\n", count);
exit(0);
}
 
 
/*
* Read a Gif file.
*/
void GifReadFile(FILE *fout, char *fname, int firstImage)
{
FILE *fp;
UBYTE *pix;
int i, gifBlockSize;
if ( (fp = fopen(fname, REABIN)) == 0) {
fprintf(stderr, "Can't open %s for reading.\n", fname);
TheEnd();
}
 
GifScreenHeader(fp, fout, firstImage);
 
/* read until , separator */
do {
switch ( i = Xgetc(fp)) {
case ',':
case '\0':
break;
case '!':
Xgetc(fp); /* the extension code */
for ( i = Xgetc(fp); i > 0; i-- ) Xgetc(fp);
while ( ( i = Xgetc(fp) ) > 0 ) {
for ( i = i ; i > 0; i-- ) Xgetc(fp);
}
break;
default:
fclose(fp);
if ( feof(fp) || i == ';' )
TheEnd1("GifReadHeader: Unexpected End of File\n");
TheEnd1("GifReadHeader: Unknown block type\n");
}
} while(i != ',');
 
if(firstImage) {
globscrn.m = gifscrn.m;
globscrn.pixbits = gifscrn.pixbits;
globscrn.bc = gifscrn.bc;
if ( globscrn.m ) {
for (i = gifMask[1+globscrn.pixbits]; i >= 0; i--) {
gifGmap[i].cmap.red = gifCmap[i].cmap.red;
gifGmap[i].cmap.green = gifCmap[i].cmap.green;
gifGmap[i].cmap.blue = gifCmap[i].cmap.blue;
}
}
if(loop) GifLoop(fout, loopcount);
}
 
ReadImageHeader(fp);
 
/*** ACTION for IMAGE */
 
if ( ( gifimage.m != 0 && globmap !=0 ) || minimize !=0 ) {
UBYTE translator[256], *p, *po;
int left, right, top, bot, i, j, k, l, hi, wi;
long dsquare, dsquare1;
hi = gifimage.height;
wi = gifimage.width;
if (( pix = (UBYTE *)malloc(wi * hi * sizeof(UBYTE)) ) == NULL )
TheEnd1("No memory for image\n");
if (debugFlag) fprintf(stderr, " decoding picture no %d\n", count);
GifDecode(fp, pix, gifimage);
gifimage.i = 0;
k = gifMask[1+globscrn.pixbits];
l = gifMask[1+gifscrn.pixbits];
for (j = 0; j <= l; j++) {
dsquare = 256*256*3;
for (i = 0; i <= k; i++) {
dsquare1 = sq(gifGmap[i].cmap.red, gifCmap[j].cmap.red) +
sq(gifGmap[i].cmap.green, gifCmap[j].cmap.green) +
sq(gifGmap[i].cmap.blue, gifCmap[j].cmap.blue);
if ( dsquare1 < dsquare ) {
dsquare = dsquare1;
translator[j]=i;
if ( dsquare == 0 ) break;
}
}
}
gifimage.m = 0;
gifscrn.pixbits = globscrn.pixbits;
if (debugFlag) fprintf(stderr, " translating picture no %d\n", count);
for (i = wi * hi -1; i>=0; i--)
pix[i]=translator[pix[i]];
if ( minimize != 0 && pixold != NULL && hi == gifimageold.height
&& wi == gifimageold.width && gifimage.top == gifimageold.top
&& gifimage.left == gifimageold.left ) {
gifimageold = gifimage;
/* First test from left to right, top to bottom */
p = pix; po = pixold;
for (i = 0; i < hi; i++ ) {
for (j = 0; j < wi; j++ ) {
if ( *p++ != *po++ ) {
left = j; top=i;
goto done;
}
}
}
if (FALSE) {
done: /* i.e. a preliminary left and top found */ ;
}
else goto alike;
/* Then test from right to left, bottom to top */
k=hi*wi-1;
p = &pix[k]; po = &pixold[k];
for (i = hi-1; i >= top; i-- ) {
for (j = wi -1; j >= 0; j-- ) {
if ( *p-- != *po-- ) {
right = j; bot=i;
goto botfound;
}
}
}
botfound:
/* The form of the differing area (not rectangle) may be slanted */
if ( right < left ) {
i = right; right = left; left = i;
}
/* Now test between top and bottom at the left hand side */
for (i = top+1; i <= bot; i++ ) {
k= i * wi;
p = &pix[k]; po = &pixold[k];
for (j = 0; j < left; j++ ) {
if ( *p++ != *po++ ) {
left = j;
break;
}
}
}
/* Finally test between bottom and top at the right hand side */
for (i = bot-1; i >= top; i-- ) {
k= (i+1) * wi-1;
p = &pix[k]; po = &pixold[k];
for (j = wi-1; j > right; j-- ) {
if ( *p-- != *po-- ) {
right = j;
break;
}
}
}
gifimage.left += left;
gifimage.top += top;
gifimage.width = right-left+1;
gifimage.height = bot-top+1;
WriteImageHeader(fout);
/* The rectangle containing diffs is transferred to the mem area of pixold */
po = pixold;
for (i = top; i <= bot; i++ ) {
p = &pix[i * wi+left];
for (j = left; j <= right; j++ ) {
*po++ = *p++;
}
}
GifEncode(fout, pixold, gifscrn.pixbits+1, gifimage.height * gifimage.width);
if (debugFlag)
fprintf(stderr, " encoded: width= %d, height = %d, left = %d, top = %d\n",
gifimage.width, gifimage.height, gifimage.left, gifimage.top);
}
else {
alike:
WriteImageHeader(fout);
gifimageold = gifimage;
GifEncode(fout, pix, gifscrn.pixbits+1, gifimage.height * gifimage.width);
if (debugFlag) fprintf(stderr, " picture re-encoded\n");
/* Undocumented feature: If two subsequent images are alike, then
send the whole image to the output stream (to keep the timing
between frames, and not confuse the viewer with empty images) */
}
free(pixold);
pixold = pix;
fputc(0, fout); /* block count of zero */
}
else {
WriteImageHeader(fout);
i = Xgetc(fp); fputc(i, fout); /* the LZW code size */
while ( ( gifBlockSize = Xgetc(fp) ) > 0 ) {
fputc(gifBlockSize, fout);
while ( gifBlockSize-- > 0 ) fputc(Xgetc(fp),fout);
}
if ( gifBlockSize == 0 ) fputc(gifBlockSize, fout);
else TheEnd1("GifPassing: Unexpected End of File\n");
}
 
fclose(fp);
}
 
 
 
/*
* read Gif header
*/
void GifScreenHeader(FILE *fp, FILE *fout, int firstTime)
{
int temp, i;
 
for(i = 0; i < 6; i++) {
temp = Xgetc(fp);
if(i == 4 && temp == '7') temp = '9';
if (firstTime) fputc(temp, fout);
}
 
gifscrn.width = GifGetShort(fp);
gifscrn.height = GifGetShort(fp);
temp = Xgetc(fp);
if (firstTime) {
GifPutShort(gifscrn.width, fout);
GifPutShort(gifscrn.height, fout);
fputc(temp, fout);
}
gifscrn.m = temp & 0x80;
gifscrn.cres = (temp & 0x70) >> 4;
gifscrn.pixbits = temp & 0x07;
 
gifscrn.bc = Xgetc(fp);
if (firstTime) {
if (debugFlag) fprintf(stderr, "First Time ... ");
if(GifBgcolor) gifscrn.bc = GifBgcolor & 0xff;
fputc(gifscrn.bc, fout);
}
 
temp = Xgetc(fp);
if (firstTime) {
fputc(temp, fout);
if ( minimize && gifscrn.bc == 0 ) {
/* Set a pseudo screen filled with the background color.
This is only done for background color index == 0 because
of Netscape and I.E.'s strange handling of backgrounds not
covered by an image.
*/
temp = gifscrn.width * gifscrn.height;
if (( pixold = (UBYTE *)malloc(temp * sizeof(UBYTE)) ) == NULL )
TheEnd1("No memory for image\n");
if (debugFlag) fprintf(stderr, "BACKGROUND = %d\n", gifscrn.bc);
while (temp > 0) pixold[--temp] = 0; /* gifscrn.bc; */
gifimageold.left = gifimageold.top = 0;
gifimageold.width = gifscrn.width;
gifimageold.height = gifscrn.height;
gifimageold.pixbits = gifscrn.pixbits;
}
}
imagec = gifPtwo[(1+gifscrn.pixbits)];
 
if (debugFlag)
fprintf(stderr, "Screen #%d: %dx%dx%d m=%d cres=%d bkgnd=%d pix=%d\n",
count, gifscrn.width, gifscrn.height, imagec, gifscrn.m, gifscrn.cres,
gifscrn.bc, gifscrn.pixbits);
 
if (gifscrn.m) {
for(i = 0; i < imagec; i++) {
gifCmap[i].cmap.red = temp = Xgetc(fp);
if (firstTime) fputc(temp, fout);
gifCmap[i].cmap.green = temp = Xgetc(fp);
if (firstTime) fputc(temp, fout);
gifCmap[i].cmap.blue = temp = Xgetc(fp);
if (firstTime) fputc(temp, fout);
 
if(firstTime && (global.trans.type==TRANS_RGB && global.trans.valid==0) )
if (global.trans.red == gifCmap[i].cmap.red &&
global.trans.green == gifCmap[i].cmap.green &&
global.trans.blue == gifCmap[i].cmap.blue) {
if(debugFlag > 1) fprintf(stderr, " Transparent match at %d\n", i);
global.trans.map = i;
global.trans.valid = 1;
}
else
if(debugFlag > 1) fprintf(stderr, "No transp. RGB=(%x,%x,%x)\n",
gifCmap[i].cmap.red, gifCmap[i].cmap.green, gifCmap[i].cmap.blue);
}
}
}
 
void ReadImageHeader(FILE *fp)
{
int tnum, i, flag;
 
gifimage.left = GifGetShort(fp);
if(global.left) gifimage.left += global.left;
 
gifimage.top = GifGetShort(fp);
if(global.top) gifimage.top += global.top;
 
gifimage.width = GifGetShort(fp);
gifimage.height = GifGetShort(fp);
flag = Xgetc(fp);
 
gifimage.i = flag & 0x40;
gifimage.pixbits = flag & 0x07;
gifimage.m = flag & 0x80;
 
imagex = gifimage.width;
imagey = gifimage.height;
tnum = gifPtwo[(1+gifimage.pixbits)];
if (debugFlag > 1)
fprintf(stderr, "Image: %dx%dx%d (%d,%d) m=%d i=%d pix=%d \n",
imagex, imagey, tnum, gifimage.left, gifimage.top,
gifimage.m, gifimage.i, gifimage.pixbits);
 
/* if there is an image cmap then read it */
if (gifimage.m) {
if(debugFlag>1)
fprintf(stderr, "DEBUG:Transferring colormap of %d colors\n",
imagec);
/*
* note below assignment, it may make the subsequent code confusing
*/
gifscrn.pixbits = gifimage.pixbits;
 
for(i = 0; i < tnum; i++) {
gifCmap[i].cmap.red = Xgetc(fp);
gifCmap[i].cmap.green = Xgetc(fp);
gifCmap[i].cmap.blue = Xgetc(fp);
}
}
gifimage.m = 0;
if ( globscrn.m && globscrn.pixbits == gifscrn.pixbits ) {
for (i = gifMask[1+globscrn.pixbits]; i >= 0; i--) {
if (gifGmap[i].cmap.red != gifCmap[i].cmap.red ||
gifGmap[i].cmap.green != gifCmap[i].cmap.green ||
gifGmap[i].cmap.blue != gifCmap[i].cmap.blue ) {
gifimage.m = 0x80;
break;
}
}
}
else gifimage.m = 0x80;
return;
}
 
void WriteImageHeader(FILE *fout)
{
int temp, i, flag;
/* compute a Gif_GCL */
 
fputc(0x21, fout);
fputc(0xF9, fout);
fputc(0x04, fout);
 
flag = global.disposal <<2;
if(global.time) flag |= 0x80;
if(global.trans.type == TRANS_RGB && global.trans.valid == 0)
gifimage.m = 0x80;
 
temp = global.trans.map;
if (gifimage.m != 0 && global.trans.type == TRANS_RGB ) {
temp = 0; /* set a default value, in case nothing found */
for (i = gifMask[1+gifscrn.pixbits]; i >= 0; i--) {
if(global.trans.red == gifCmap[i].cmap.red &&
global.trans.green == gifCmap[i].cmap.green &&
global.trans.blue == gifCmap[i].cmap.blue) {
if(debugFlag > 1) fprintf(stderr, " Transparent match at %d\n", i);
temp = i;
flag |= 0x01;
}
}
}
else if(global.trans.valid) flag |= 0x01;
fputc(flag, fout);
 
GifPutShort(global.time, fout); /* the delay speed - 0 is instantaneous */
 
fputc(temp, fout); /* the transparency index */
if(debugFlag > 1) {
fprintf(stderr, "GCL: delay %d", global.time);
if(flag && 0x1) fprintf(stderr, " Transparent: %d", temp);
fputc('\n', stderr);
}
 
fputc(0, fout);
/* end of Gif_GCL */
 
fputc(',', fout); /* image separator */
GifPutShort(gifimage.left , fout);
GifPutShort(gifimage.top , fout);
GifPutShort(gifimage.width , fout);
GifPutShort(gifimage.height, fout);
fputc(gifscrn.pixbits | gifimage.i | gifimage.m, fout);
 
if ( gifimage.m ) {
for(i = 0; i < imagec; i++) {
fputc(gifCmap[i].cmap.red, fout);
fputc(gifCmap[i].cmap.green, fout);
fputc(gifCmap[i].cmap.blue, fout);
}
if(debugFlag) fprintf(stderr, "Local %d color map for picture #%d\n",
imagec, count);
}
}
 
 
void GifComment(FILE *fout, char *string)
{
int len;
 
if( (len = strlen(string)) > 254 ) fprintf(stderr,
"GifComment: String too long ; dropped\n");
else if ( len > 0 ) {
/* Undocumented feature:
Empty comment string means no comment block in outfile */
fputc(0x21, fout);
fputc(0xFE, fout);
fputc(len, fout);
fputs(string, fout);
fputc(0, fout);
}
return;
}
 
/*
* Write a Netscape loop marker.
*/
void GifLoop(FILE *fout, unsigned int repeats)
{
 
fputc(0x21, fout);
fputc(0xFF, fout);
fputc(0x0B, fout);
fputs("NETSCAPE2.0", fout);
 
fputc(0x03, fout);
fputc(0x01, fout);
GifPutShort(repeats, fout); /* repeat count */
 
fputc(0x00, fout); /* terminator */
 
if(debugFlag) fprintf(stderr, "Wrote loop extension\n");
}
 
 
void CalcTrans(char *string)
{
if(string[0] != '#') {
global.trans.type = TRANS_MAP;
global.trans.map = atoi(string);
global.trans.valid = 1;
}
else {
/* it's an RGB value */
int r, g, b;
string++;
if (debugFlag > 1) fprintf(stderr, "String is %s\n", string);
if(3 == sscanf(string, "%2x%2x%2x", &r, &g, &b)) {
global.trans.red = r;
global.trans.green = g;
global.trans.blue = b;
global.trans.type = TRANS_RGB;
global.trans.valid = 0;
if(debugFlag > 1)
fprintf(stderr, "Transparent RGB=(%x,%x,%x) = (%d,%d,%d)\n",
r, g, b, r, g, b);
}
}
if(debugFlag > 1)
fprintf(stderr, "DEBUG:CalcTrans is %d\n", global.trans.type);
}
 
void SetOffset(char *string)
{
int npar, offX, offY;
char sep;
if ( (npar = sscanf(string, "%d%c%d", &offX, &sep, &offY)) == 3 ) {
/* set the offset */
global.left = offX;
global.top = offY;
if(debugFlag > 1) fprintf(stderr, "Offset set to %d,%d\n",
global.left, global.top);
return;
}
fprintf(stderr, "Offset string: '%s'; fields = %d\n", string, npar);
TheEnd1("Couldn't parse offset values.\n");
}
 
void TheEnd()
{
exit(0);
}
 
void TheEnd1(char *p)
{
fprintf(stderr, "Image #%d: %s", count, p);
TheEnd();
}
 
void Usage()
{
fprintf(stderr, "\nUsage: whirlgif %s\n %s\n %s\n",
"[-o outfile] [-loop [count]] [-time #delay]",
"\t-disp [ none | back | prev | not ]",
"\t[ -i listfile] file1 [ -time #delay] file2 ...");
exit(0);
}
 
UBYTE Xgetc(FILE *fin)
{
int i;
if ( ( i = fgetc(fin) ) == EOF ) {
TheEnd1("Unexpected EOF in input file\n");
}
return(i & 0xff);
}
/trunk/wims/src/Misc/whirlgif/README
0,0 → 1,36
README file for Whirlgif vers 3.0x
 
To create your own version of the minimizing version of whirlgif,
proceed as follows:
1) Inspect the Makefile and perhaps make apropriate changes.
2) Run make. If you encounter problems, please mailto:dino@danbbs.dk
3) Read the (pure ascii) file whirlgif.man or the formated version
with the command: nroff -man whirlgif.1 | more
4) Take a look at the page
http://www.danbbs.dk/~dino/whirlgif/guestbk.htm
where user comments and bug reports are collected - and perhaps also
http://www.danbbs.dk/~dino/whirlgif/examples.html
5) Create your own animated GIFs or optimize the size of the ones
you have created already.
 
This version of whirlgif contains an angorithm for en-coding GIF
files. There may exist places in this world, where persons or
organizations claim to have a patent covering *all* possible
implementations of the LZW-algorithm. Such persons or organizations
are requested to complain by E-mail to dino@danbbs.dk, so that their
claim can be publicized in connection with
http://www.danbbs.dk/~dino/whirlgif/disclaimer.html
quickly after it has been received.
 
Hans Dinsen-Hansen, the owner of above page, claims the right
to comment on any such protests and to publish them in a form
which references his comments.
 
This software is provided solely "as is". The authors disclaim all
responsibility and liability with respect to this software's usage,
its effect upon hardware, or its effect upon computer systems. Apart
from this, the code is free for all.
 
There are copyright notices in the program texts.
 
Version 3.02 is also available from http://sources.isc.org/apps/graphics/
/trunk/wims/src/Misc/whirlgif/Makefile.dist
0,0 → 1,93
# Makefile for the Whirlgif utilities.
# Comments for the Amiga is kindly supplied by
# Ron Jensen <rjensen@konnections.com>
# If you use Visual C++, all '.o' must be changed to '.obj'
###########################################################
# Define target names
#
# Win32:
#TARG_WH = whirlgif.exe
#
# Unix, Amiga:
TARG_WH = whirlgif
 
###########################################################
# Define install dirs
#
# Win32: perhaps install by hand.
#INSTDIRS = C:
#BINDIR = DOS
#
# Amiga: Install by hand
# Unix: perhaps change according to your flavor.
INSTDIRS = /usr/local
BINDIR = bin
MANDIR = man/cat1
 
###########################################################
# Choose the compiler.
#
# GNU C
#CC = gcc
# Amiga: SASC
# CC = sc
# standard
CC = cc
 
###########################################################
# Optimization flags
#
# DJGPP:
#OPTIMIZE= -fcombine-regs -O
# for Amiga's SASC compiler remove Optimization flags.
# standard:
OPTIMIZE = -O
 
###########################################################
# Debug flags
#
DEBUG = -g
#DEBUG = -ggdb
 
###########################################################
# CFLAGS
#
# If you need <strings.h>, uncomment next line
#STRS_H = -D_USE_STRINGS_H
# If you use DJGPP or Visual C++ uncomment next line
#VIS_GNU_W32 = -D_FOPEN_TXT_OR_BIN
#
# If you use GNUcc and like warnings uncomment next line
# WARNINGS = -Wall
# for the Amiga:
# CFLAGS = DATA=FAR MATH=IEEE CPU=68020 PARAMETERS=BOTH ANSI STRICT NOICONS IGNORE=51
# standard:
CFLAGS = $(DEBUG) $(OPTIMIZE) $(VIS_GNU_W32) $(STRS_H) $(WARNINGS)
 
all: $(TARG_WH)
 
$(TARG_WH): whirlgif.o gifencod.o gifdecod.o
$(CC) $(CFLAGS) -o $(TARG_WH) whirlgif.o gifencod.o gifdecod.o
#for the Amiga: remove above line and use below:
# slink NOICONS from lib:c.o+whirlgif.o+gifencod.o+gifdecod.o to WhirlGif3 LIB LIB:scmieee.lib LIB:sc.lib LIB:amiga.lib
 
clean:
rm -f $(TARG_WH) *.o
 
# under DOS/W32 as well as on the Amiga it is probably best to install by hand
# under Unix perhaps change below commands for your flavor and local
# traditions. The install(1) used here is sometimes located in /usr/ucb
 
install: all
nroff -man whirlgif.1 > $(INSTDIRS)/$(MANDIR)/whirlgif.0 ;\
install -cs -g bin -m 555 -o bin $(TARG_WH) $(INSTDIRS)/$(BINDIR)
 
# DEPENDECIES FOLLOW:
gifdecod.o: whirlgif.h gifdecod.c
$(CC) $(CFLAGS) -c gifdecod.c
 
gifencod.o: whirlgif.h gifencod.c
$(CC) $(CFLAGS) -c gifencod.c
 
whirlgif.o: whirlgif.h whirlgif.c
$(CC) $(CFLAGS) -c whirlgif.c
/trunk/wims/src/Misc/whirlgif/gifencod.c
0,0 → 1,294
/*
* gifencode.c
*
* Copyright (c) 1997,1998 by Hans Dinsen-Hansen
* The algorithms are inspired by those of gifcode.c
* Copyright (c) 1995,1996 Michael A. Mayer
* All rights reserved.
*
* This software may be freely copied, modified and redistributed
* without fee provided that above copyright notices are preserved
* intact on all copies and modified copies.
*
* There is no warranty or other guarantee of fitness of this software.
* It is provided solely "as is". The author(s) disclaim(s) all
* responsibility and liability with respect to this software's usage
* or its effect upon hardware or computer systems.
*
* The Graphics Interchange format (c) is the Copyright property of
* Compuserve Incorporated. Gif(sm) is a Service Mark property of
* Compuserve Incorporated.
*
*
* Implements GIF encoding by means of a tree search.
* --------------------------------------------------
*
* - The string table may be thought of being stored in a "b-tree of
* steroids," or more specifically, a {256,128,...,4}-tree, depending on
* the size of the color map.
* - Each (non-NULL) node contains the string table index (or code) and
* {256,128,...,4} pointers to other nodes.
* - For example, the index associated with the string 0-3-173-25 would be
* stored in:
* first->node[0]->node[3]->node[173]->node[25]->code
*
* - Speed and effectivity considerations, however, have made this
* implementation somewhat obscure, because it is costly to initialize
* a node-array where most elements will never be used.
* - Initially, a new node will be marked as terminating, TERMIN.
* If this node is used at a later stage, its mark will be changed.
* - Only nodes with several used nodes will be associated with a
* node-array. Such nodes are marked LOOKUP.
* - The remaining nodes are marked SEARCH. They are linked together
* in a search-list, where a field, NODE->alt, points at an alternative
* following color.
* - It is hardly feasible exactly to predict which nodes will have most
* used node pointers. The theory here is that the very first node as
* well as the first couple of nodes which need at least one alternative
* color, will be among the ones with many nodes ("... whatever that
* means", as my tutor in Num. Analysis and programming used to say).
* - The number of possible LOOKUP nodes depends on the size of the color
* map. Large color maps will have many SEARCH nodes; small color maps
* will probably have many LOOKUP nodes.
*/
 
#include "whirlgif.h"
 
#define BLOKLEN 255
#define BUFLEN 1000
 
 
int chainlen = 0, maxchainlen = 0, nodecount = 0, lookuptypes = 0, nbits;
short need = 8;
GifTree *empty[256], GifRoot = {LOOKUP, 0, 0, empty, NULL, NULL},
*topNode, *baseNode, **nodeArray, **lastArray;
 
extern unsigned int debugFlag, verbose;
extern int count;
 
void GifEncode(FILE *fout, UBYTE *pixels, int depth, int siz)
{
GifTree *first = &GifRoot, *newNode, *curNode;
UBYTE *end;
int cc, eoi, next, tel=0;
short cLength;
 
char *pos, *buffer;
 
empty[0] = NULL;
need = 8;
 
nodeArray = empty;
memmove(++nodeArray, empty, 255*sizeof(GifTree **));
if (( buffer = (char *)malloc((BUFLEN+1)*sizeof(char))) == NULL )
TheEnd1("No memory for writing");
buffer++;
 
 
pos = buffer;
buffer[0] = 0x0;
 
cc = (depth == 1) ? 0x4 : 1<<depth;
fputc((depth == 1) ? 2 : depth, fout);
eoi = cc+1;
next = cc+2;
 
cLength = (depth == 1) ? 3 : depth+1;
 
if (( topNode = baseNode = (GifTree *)malloc(sizeof(GifTree)*4094)) == NULL )
TheEnd1("No memory for GIF-code tree");
if (( nodeArray = first->node = (GifTree **)malloc(256*sizeof(GifTree *)*noOfArrays)) == NULL )
TheEnd1("No memory for search nodes");
lastArray = nodeArray + ( 256*noOfArrays - cc);
ClearTree(cc, first);
 
pos = AddCodeToBuffer(cc, cLength, pos);
 
end = pixels+siz;
curNode = first;
while(pixels < end) {
 
if ( curNode->node[*pixels] != NULL ) {
curNode = curNode->node[*pixels];
tel++;
pixels++;
chainlen++;
continue;
} else if ( curNode->typ == SEARCH ) {
newNode = curNode->nxt;
while ( newNode->alt != NULL ) {
if ( newNode->ix == *pixels ) break;
newNode = newNode->alt;
}
if (newNode->ix == *pixels ) {
tel++;
pixels++;
chainlen++;
curNode = newNode;
continue;
}
}
 
/* ******************************************************
* If there is no more thread to follow, we create a new node. If the
* current node is terminating, it will become a SEARCH node. If it is
* a SEARCH node, and if we still have room, it will be converted to a
* LOOKUP node.
*/
newNode = ++topNode;
switch (curNode->typ ) {
case LOOKUP:
newNode->nxt = NULL;
newNode->alt = NULL,
curNode->node[*pixels] = newNode;
break;
case SEARCH:
if ( nodeArray != lastArray ) {
nodeArray += cc;
curNode->node = nodeArray;
curNode->typ = LOOKUP;
curNode->node[*pixels] = newNode;
curNode->node[(curNode->nxt)->ix] = curNode->nxt;
lookuptypes++;
newNode->nxt = NULL;
newNode->alt = NULL,
curNode->nxt = NULL;
break;
}
/* otherwise do as we do with a TERMIN node */
case TERMIN:
newNode->alt = curNode->nxt;
newNode->nxt = NULL,
curNode->nxt = newNode;
curNode->typ = SEARCH;
break;
default:
fprintf(stderr, "Silly node type: %d\n", curNode->typ);
}
newNode->code = next;
newNode->ix = *pixels;
newNode->typ = TERMIN;
newNode->node = empty;
nodecount++;
/*
* End of node creation
* ******************************************************
*/
if (debugFlag) {
if (curNode == newNode) fprintf(stderr, "Wrong choice of node\n");
if ( curNode->typ == LOOKUP && curNode->node[*pixels] != newNode ) fprintf(stderr, "Wrong pixel coding\n");
if ( curNode->typ == TERMIN ) fprintf(stderr, "Wrong Type coding; frame no = %d; pixel# = %d; nodecount = %d\n", count, tel, nodecount);
}
pos = AddCodeToBuffer(curNode->code, cLength, pos);
if ( chainlen > maxchainlen ) maxchainlen = chainlen;
chainlen = 0;
if(pos-buffer>BLOKLEN) {
buffer[-1] = BLOKLEN;
fwrite(buffer-1, 1, BLOKLEN+1, fout);
buffer[0] = buffer[BLOKLEN];
buffer[1] = buffer[BLOKLEN+1];
buffer[2] = buffer[BLOKLEN+2];
buffer[3] = buffer[BLOKLEN+3];
pos -= BLOKLEN;
}
curNode = first;
 
if(next == (1<<cLength)) cLength++;
next++;
 
if(next == 0xfff) {
ClearTree(cc,first);
pos = AddCodeToBuffer(cc, cLength, pos);
if(pos-buffer>BLOKLEN) {
buffer[-1] = BLOKLEN;
fwrite(buffer-1, 1, BLOKLEN+1, fout);
buffer[0] = buffer[BLOKLEN];
buffer[1] = buffer[BLOKLEN+1];
buffer[2] = buffer[BLOKLEN+2];
buffer[3] = buffer[BLOKLEN+3];
pos -= BLOKLEN;
}
next = cc+2;
cLength = (depth == 1)?3:depth+1;
}
}
 
pos = AddCodeToBuffer(curNode->code, cLength, pos);
if(pos-buffer>BLOKLEN-3) {
buffer[-1] = BLOKLEN-3;
fwrite(buffer-1, 1, BLOKLEN-2, fout);
buffer[0] = buffer[BLOKLEN-3];
buffer[1] = buffer[BLOKLEN-2];
buffer[2] = buffer[BLOKLEN-1];
buffer[3] = buffer[BLOKLEN];
buffer[4] = buffer[BLOKLEN+1];
pos -= BLOKLEN-3;
}
pos = AddCodeToBuffer(eoi, cLength, pos);
pos = AddCodeToBuffer(0x0, -1, pos);
buffer[-1] = pos-buffer;
 
fwrite(buffer-1, pos-buffer+1, 1, fout);
free(buffer-1); free(first->node); free(baseNode);
if (debugFlag) fprintf(stderr, "pixel count = %d; nodeCount = %d lookup nodes = %d\n", tel, nodecount, lookuptypes);
return;
 
}
 
void ClearTree(int cc, GifTree *root)
{
int i;
GifTree *newNode, **xx;
 
if (debugFlag>1) fprintf(stderr, "Clear Tree cc= %d\n", cc);
if (debugFlag>1) fprintf(stderr, "nodeCount = %d lookup nodes = %d\n", nodecount, lookuptypes);
maxchainlen=0; lookuptypes = 1;
nodecount = 0;
nodeArray = root->node;
xx= nodeArray;
for (i = 0; i < noOfArrays; i++ ) {
memmove (xx, empty, 256*sizeof(GifTree **));
xx += 256;
}
topNode = baseNode;
for(i=0; i<cc; i++) {
root->node[i] = newNode = ++topNode;
newNode->nxt = NULL;
newNode->alt = NULL;
newNode->code = i;
newNode->ix = i;
newNode->typ = TERMIN;
newNode->node = empty;
nodecount++;
}
}
 
char *AddCodeToBuffer(int code, short n, char *buf)
{
int mask;
 
if(n<0) {
if(need<8) {
buf++;
*buf = 0x0;
}
need = 8;
return buf;
}
 
while(n>=need) {
mask = (1<<need)-1;
*buf += (mask&code)<<(8-need);
buf++;
*buf = 0x0;
code = code>>need;
n -= need;
need = 8;
}
if(n) {
mask = (1<<n)-1;
*buf += (mask&code)<<(8-need);
need -= n;
}
return buf;
}
/trunk/wims/src/Misc/whirlgif/whirlgif.h
0,0 → 1,176
/*
* whirlgif.h
*
* Copyright (c) 1997,1998,1999 by Hans Dinsen-Hansen
* Copyright (c) 1995,1996 by Kevin Kadow
* Copyright (c) 1990,1991,1992 by Mark Podlipec.
* All rights reserved.
*
* This software may be freely copied, modified and redistributed
* without fee provided that this copyright notice is preserved
* intact on all copies and modified copies.
*
* There is no warranty or other guarantee of fitness of this software.
* It is provided solely "as is". The author(s) disclaim(s) all
* responsibility and liability with respect to this software's usage
* or its effect upon hardware or computer systems.
*
* The Graphics Interchange format (c) is the Copyright property of
* Compuserve Incorporated. Gif(sm) is a Service Mark property of
* Compuserve Incorporated.
*
*/
 
#define DA_REV 3.04
 
/* common includes */
#include <stdio.h>
#include <stdlib.h>
 
#ifdef _USE_STRINGS_H
#include <strings.h>
#else
#include <string.h>
#endif
 
#ifdef _FOPEN_TXT_OR_BIN
#define WRIBIN "wb"
#define REATXT "rt"
#define REABIN "rb"
#else
/* Usually there is no need to distinguish between binary and txt */
#define WRIBIN "w"
#define REATXT "r"
#define REABIN "r"
#endif
 
#ifndef TRUE
#define TRUE 1
#endif
#ifndef FALSE
#define FALSE 0
#endif
 
/* define constants and defaults */
/* Default amount of inter-frame time */
#define DEFAULT_TIME 10
/* If set to 1, Netscape 'loop' code will be added by default */
#define DEFAULT_LOOP 0
/* If set to 1, use the colormaps from all images, not just the first */
#define DEFAULT_USE_COLORMAP 0
 
/* Used in calculating the transparent color */
#define TRANS_NONE 1
#define TRANS_RGB 2
#define TRANS_MAP 3
 
#define DISP_NONE 0
#define DISP_NOT 1
#define DISP_BACK 2
#define DISP_PREV 3
#define DEFAULT_DISPOSAL DISP_NONE
/* set default disposal method here to any of the DISP_XXXX values */
 
#define BIGSTRING 256
#define MAXVAL 4096 /* maxval of lzw coding size */
#define MAXVALP 4096
#define TERMIN 'T'
#define LOOKUP 'L'
#define SEARCH 'S'
#define noOfArrays 20
/* defines the amount of memory set aside in the encoding for the
* LOOKUP type nodes; for a 256 color GIF, the number of LOOKUP
* nodes will be <= noOfArrays, for a 128 color GIF the number of
* LOOKUP nodes will be <= 2 * noOfArrays, etc. */
 
/* define shorthand for various types */
#define LONG int
#define ULONG unsigned int
#define BYTE char
#define UBYTE unsigned char
#define SHORT short
#define USHORT unsigned short
#define WORD short int
#define UWORD unsigned short int
 
 
/* definition of various structures */
typedef struct Transparency {
int type;
UBYTE valid;
UBYTE map;
UBYTE red;
UBYTE green;
UBYTE blue;
} Transparency;
 
typedef struct Global {
Transparency trans;
int left;
int top;
unsigned int time;
unsigned short disposal;
} Global;
 
typedef struct GifScreenHdr {
int width;
int height;
UBYTE m;
UBYTE cres;
UBYTE pixbits;
UBYTE bc;
UBYTE aspect;
} GifScreenHdr;
 
typedef union GifColor {
struct cmap {
UBYTE red;
UBYTE green;
UBYTE blue;
UBYTE pad;
} cmap;
ULONG pixel;
} GifColor;
 
typedef struct GifImageHdr {
int left;
int top;
int width;
int height;
UBYTE m;
UBYTE i;
UBYTE pixbits;
UBYTE reserved;
} GifImageHdr;
 
typedef struct GifTree {
char typ; /* terminating, lookup, or search */
int code; /* the code to be output */
UBYTE ix; /* the color map index */
struct GifTree **node, *nxt, *alt;
} GifTree;
 
/* define inline functions */
#define GifPutShort(i, fout) {fputc(i&0xff, fout); fputc(i>>8, fout);}
#define GifGetShort(fin) (Xgetc(fin) | Xgetc(fin)<<8)
 
/* forward declaration of the functions */
char *AddCodeToBuffer(int, short, char *);
void CalcTrans(char *);
void ClearTree(int, GifTree *);
void GifClearTable();
void GifComment(FILE *, char *);
void GifDecode(FILE *, UBYTE *, GifImageHdr);
void GifEncode(FILE *, UBYTE *, int, int);
void GifLoop(FILE *, unsigned int);
void GifReadFile(FILE *, char *, int);
void GifScreenHeader(FILE *, FILE *, int);
UBYTE *GifSendData(UBYTE *, int, UBYTE *);
void ReadImageHeader(FILE *);
void SetOffset(char *);
long sq(UBYTE, UBYTE);
void TheEnd();
void TheEnd1(char *);
void Usage();
void WriteImageHeader(FILE *);
UBYTE Xgetc(FILE *);
/trunk/wims/src/Misc/whirlgif/Makefile.in
0,0 → 1,28
# @configure_input@
 
wims_home=../../..
cc=@CC@
cflags=@CFLAGS@ -Wall @STATIC_LIB@
defines=@DEFINES@
 
all: whirlgif
 
whirlgif: whirlgif.o gifencod.o gifdecod.o
$(cc) $(cflags) -o whirlgif whirlgif.o gifencod.o gifdecod.o
 
clean:
rm -f whirlgif *.o
 
distclean: clean
rm -f $(wims_home)/other/bin/whirlgif
rm -f Makefile
 
# DEPENDECIES FOLLOW:
gifdecod.o: whirlgif.h gifdecod.c
$(cc) $(cflags) -c gifdecod.c
 
gifencod.o: whirlgif.h gifencod.c
$(cc) $(cflags) -c gifencod.c
 
whirlgif.o: whirlgif.h whirlgif.c
$(cc) $(cflags) -c whirlgif.c
/trunk/wims/src/Misc/whirlgif/gifdecod.c
0,0 → 1,162
/*
* gifdecode.c
*
* Copyright (c) 1997,1998,1999 by Hans Dinsen-Hansen
* Partly inspired by Helene Schulerud's inversrl_imageb.c of 13.2.1991.
* Copyright (c) 1995,1996 by Kevin Kadow
* Copyright (c) 1990,1991,1992,1993 by Mark Podlipec
* All rights reserved.
*
* This software may be freely copied, modified and redistributed
* without fee provided that this copyright notice is preserved
* intact on all copies and modified copies.
*
* There is no warranty or other guarantee of fitness of this software.
* It is provided solely "as is". The author(s) disclaim(s) all
* responsibility and liability with respect to this software's usage
* or its effect upon hardware or computer systems.
*
* The Graphics Interchange format (c) is the Copyright property of
* Compuserve Incorporated. Gif(sm) is a Service Mark property of
* Compuserve Incorporated.
*
*/
 
#include <stdio.h>
#include "whirlgif.h"
 
extern unsigned int debugFlag, verbose;
extern int count;
 
ULONG codeSize, expected, imgsize, mask, old, picI, rootCodeSize, first[MAXVAL];
UBYTE *topGifBuff, *picture, gifBuff[MAXVAL], last[MAXVAL];
int imgheight, imgwidth, interlaced, pass,
step[5]={7,7,3,1,0}, start[5]= {0,4,2,1,0};
 
void GifDecode(FILE *fp, UBYTE *pix, GifImageHdr gifimage)
{ UBYTE *chPos, firstCodeOut = 0, charBuff[256];
ULONG CLEAR, EOI, bits = 0, code = 0, codeFeched, buffCount = 0;
int need = 0;
 
interlaced = gifimage.i;
imgwidth = gifimage.width;
imgheight = gifimage.height;
imgsize = imgwidth * imgheight;
picture = pix;
pass = picI = 0;
if ( debugFlag > 1 )
fprintf(stderr, "(interlaced,imgwidth,imgheight,imgsize)=(%d,%d,%d,%d)\n",
interlaced, imgwidth, imgheight, imgsize);
 
rootCodeSize = Xgetc(fp);
CLEAR = 1 << rootCodeSize;
EOI = CLEAR + 1;
 
GifClearTable();
 
if ( (buffCount = Xgetc(fp)) == 0 ) {
sprintf(charBuff, "End of image # %d before it began!\n", count);
TheEnd1(charBuff);
}
 
while(buffCount > 0) {
if ( fread(charBuff, 1, buffCount, fp) != buffCount ) {
sprintf(charBuff, "Premature end of file; Image # %d\n", count);
TheEnd1(charBuff);
}
for(chPos = charBuff; buffCount-- > 0; chPos++) {
need += (int) *chPos << bits;
bits += 8;
while (bits >= codeSize) {
code = need & mask;
need >>= codeSize;
bits -= codeSize;
if(code > expected)
TheEnd1("Neither LZW nor RunLength (new code != expected)\n");
if (code == EOI) {
if (debugFlag > 1) fprintf(stderr, "Image # %d ends; buffCount=%d\n",
count, buffCount);
goto skipRest;
}
 
if(code == CLEAR) {
GifClearTable();
continue;
}
if(old == MAXVAL) { /* i.e. first code after clear table */
pix = GifSendData(pix, 1, &last[code]);
firstCodeOut = last[code];
old = code;
continue;
}
codeFeched = code;
if(code == expected) {
*topGifBuff++ = firstCodeOut;
code = old;
}
while(code > CLEAR) {
*topGifBuff++ = last[code];
code = first[code];
}
 
*topGifBuff++ = firstCodeOut = last[code];
first[expected] = old;
last[expected] = firstCodeOut;
if(expected < MAXVAL) expected++;
if(((expected & mask) == 0) && (expected < MAXVAL)) {
codeSize++;
mask += expected;
}
old = codeFeched;
pix = GifSendData(pix, topGifBuff - gifBuff, gifBuff);
topGifBuff = gifBuff;
 
} /* end of extracting codes */
} /* end of reading a block of data */
if ( (buffCount = Xgetc(fp)) == 0 ) {
sprintf(charBuff, "End of image # %d without EOI\n", count);
TheEnd1(charBuff);
}
}
 
skipRest:
if (debugFlag) fprintf(stderr, "Ending GifDecode, written: %d=%d\n",
interlaced && (pix-picture == 0) ? imgsize : pix - picture, imgsize);
return ;
}
 
UBYTE *GifSendData(UBYTE *pix, int bytes, UBYTE source[])
 
{ int j=0;
for(j = bytes - 1; j >= 0; j--) {
picI++;
*pix = source[j]; pix++;
if ( interlaced && (picI % imgwidth == 0) ) {
picI += ( imgwidth * step[pass]);
if (picI >= imgsize) {
picI = start[++pass] * imgwidth;
if ( debugFlag > 1 )
fprintf(stderr, "De-interlacing: (picI,pass,start[pass])=(%d,%d,%d)\n",
picI, pass, start[pass]);
}
pix = &picture[picI];
}
}
return(pix);
}
 
void GifClearTable()
{ int i, maxi;
maxi = 1 << rootCodeSize;
expected = maxi + 2;
if (debugFlag > 1 ) fprintf(stderr, "Initing Table...");
old = MAXVAL;
codeSize = rootCodeSize + 1;
mask = (1<<codeSize) - 1;
 
for(i = 0; i < maxi; i++) {
first[i] = MAXVAL;
last[i] = i & 0xff;
}
topGifBuff = gifBuff;
}
/trunk/wims/src/Misc/whirlgif/whirlgif.man
0,0 → 1,205
WHIRLGIF(1) (February 1999) WHIRLGIF(1)
 
NAME
whirlgif - reads a series of GIF87a or GIF89a files, and produces one
single GIF89a file composed of those images.
 
SYNOPSIS
whirlgif [global-options] [file-options {GIFfile|-i incfile
 
The global-options are one or more of:
-v
-trans index
-background {color|index}
-time delay
-globmap
-minimize
-o outfile
-loop [count]
-comment comment
-disp method
 
The file-options are one or more of:
-off x:y-pair
-trans {color|index}
-time delay
-disp method
 
 
DESCRIPTION
When Whirlgif processes a series of GIF files, the first file defines
the so called screen size and the background color of the resulting
GIF. The background color may be changed by an option.
 
The input files may be in either GIF87a or GIF89a format. If a GIF
file contains more than one image, only the first image is read. To
help saving bandwidth on the internet, there are options for forcing
all GIFs in the series to use the same color map and to create sub-
GIFs which only give the difference between one image and the next.
 
OPTIONS
-v Verbose mode. Output is written to the error out file. This is
a global option and must be given before any GIFfiles.
 
-trans index|color
Set the color identified with index or the color code #xxxxxx,
where xxxxxx is a hex RGB index to be transparent. When this
option is used as a file-option, its setting is valid for all
subsequent GIFfiles, until it is used again.
 
 
-background index
Set the color identified with its index to be the background
color, possibly overriding the background color defined in the
first GIFfile. This is a global option and must be given before
any GIFfiles.
 
-time delay
Defines inter-frame timing in units of 1/100 second. Whenever a
value of delay is set by means of the -time option, this value
will be valid for the following GIFfiles until a new value is
set.
 
-globmap
Use the color map of the first GIFfile as the global color map
throughout the outfile. If the color maps of subsequent GIFfiles
contain colors that are not in the global color map, the nearest
colors in a 3 dimensional RGB space will be chosen. This is a
global option and must be given before any GIFfiles.
 
-minimize
Only the smallest rectangles covering the difference between two
GIFfiles will be output. All GIFfiles must be of the same size
and have the same offset. This is tested, and may result in an
error message. This is a global option and must be given before
any GIFfiles.
 
As a side effect, the -minimize option forces the same
(global)color map to be used throughout the outfile.
 
-o outfile
Write the results to outfile This is a global option and must be
given before any GIFfiles.
 
-loop [count]
Add the Netscape 'loop' extension. If count is omitted, 0 (zero)
is assumed. This is a global option and must be given before any
GIFfiles.
 
-comment comment
Define a Comment Extension block within the resulting GIF file.
This block will be written at the end of the file. A comment
block can be at most 254 characters long, including line shifts.
It may be used for copyright notices. This is a global option
and must be given before any GIFfiles.
 
-disp method
Sets the image 'disposal' method. When this option is used as a
file-option, its setting is valid for all subsequent GIFfiles,
until it is used again. The methods may be one of the keywords:
 
none No disposal specified.
 
back Restore to background color. Fill the image's space with
the background color.
 
prev Restore to previous, restores the screen area to what was
there before. Netscape has a problem with this option.
 
not Do not dispose. The default, the next image will overlay
this one.
 
-i incfile
Read a list of names from incfile
 
-off x:y-pair
Sets the position of the next GIFfile or the set of GIFfiles in
an incfile.
 
The x:y-pair consists of two integers, separated by one character
which cannot be part of a number. A colon, ':' is preferred.
The two integers define the position of the upper left corner of
the next image within the 'screen' of the resulting GIF. The
integers are added to a possible offset within the next GIFfile.
One or both may be negative. Negative numbers will re-position
an image upwards and/or to the left within its own 'screen'. The
'screen' of a GIF is the term used in the GIF definitions for the
area, a GIF covers.
 
TIPS
If you don't specify an output file, the GIF will be sent to stdout.
This is a good thing if you're using whirlgif in a CGI script, but
could cause problems if you run from a terminal and forget to redirect
stdout.
 
The output file (if any) and the loop option MUST be specified before
any GIF images.
 
You can specify several delay statements on the command line to change
the delay between images in the middle of an animation, e.g.
 
whirlgif -time 25 a.gif b.gif c.gif -time 100 d.gif -time 25 e.gif f.gif
 
Usually, the bacground color is defined by the first GIFfile, and
often it is index number zero (0). If you are animating with a
transparent color, a good method is usually to use the same color as
the background.
 
It is possible, but may cause problems for some programs, to specify
several transparency statements on the command line.
 
The size of the resulting GIF and the global color map is defined by
the first GIFfile. If you have a large background GIF and have
several smaller GIFs, you may make some kind of a bulletin board by a
command of the following form:
 
whirlgif large.gif -off 77:44 small1.gif -off 22:99 small2.giff ...
 
You may build in some timing between the small GIFs.
 
LIMITATIONS
The loop 'count' is ineffective because Netspcape always loops
infinitely.
 
BUGS
Whirlgif should be able to specify delay and offset in an 'incfile'
list (see next bug).
 
Does not handle filenames starting with a - (hypen), except in
'incfile'.
 
TODO
- Make a graphic interface. Perhaps by re-implementing the
algorithms in Java(tm).
 
- option for other choices of unification methods for colormaps.
The nearest point in the 3 dimensional RGB color space may not be the
best choice.
 
- define extension block in order to have frame lists.
 
- define single-letter options and use getopt(3) like most UNIX
programs.
 
AVAILABILITY
This program is available via the archive for comp.soruces.unix
http://sources.isc.org/ (and also via
http://www.danbbs.dk/~dino/whirlgif/ ).
 
AUTHORs
V3.00 - 3.04 Hans Dinsen-Hansen <dino@danbbs.dk>
Based on Whirlgif V2.02 by Kevin Kadow <kadokev@msg.net>
& Hans Dinsen-Hansen <dino@danbbs.dk>
which is again based on 'txtmerge' written by:
Mark Podlipec <podlipec@BayNetworks.com>
Man Page by Mintak Ng <mintak@hitak.com>
& Hans Dinsen-Hansen <dino@danbbs.dk>
 
COPYRIGHT NOTICES
The Graphics Interchange Format (c) is the Copyright property of
Compuserve Incorporated. GIF(sm) is a Service Mark property of
Compuserve Incorporated.
 
See other Copyright notices in the accompanying texts.
 
WHIRLGIF(1) (February 1999) WHIRLGIF(1)