/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) |