1 //
2 // The loading routine for gif images was extracted from the gif-loader
3 // of the xv-package. It was enclosed into a C++-class and enhanced for some
4 // features to query possible extension data, which can be used to store
5 // the additional setup informations and copyright notes.
6 //
7 // Here is the original note, that once was contained in the loader
8 //
9
10 /*
11 * xvgif.c - GIF loading code for 'xv'. Based strongly on...
12 *
13 * gif2ras.c - Converts from a Compuserve GIF (tm) image to a Sun Raster image.
14 *
15 * Copyright (c) 1988, 1989 by Patrick J. Naughton
16 *
17 * Author: Patrick J. Naughton
18 * naughton@wind.sun.com
19 *
20 * Permission to use, copy, modify, and distribute this software and its
21 * documentation for any purpose and without fee is hereby granted,
22 * provided that the above copyright notice appear in all copies and that
23 * both that copyright notice and this permission notice appear in
24 * supporting documentation.
25 *
26 * This file is provided AS IS with no warranties of any kind. The author
27 * shall have no liability with respect to the infringement of copyrights,
28 * trade secrets or any patents by this file or any part thereof. In no
29 * event will the author be liable for any lost revenue or profits or
30 * other special, indirect and consequential damages.
31 *
32 */
33
34 #include <stdlib.h>
35 #include <stdio.h>
36 #include <string.h>
37
38 #include "gif_image.H"
39
40 #define False 0
41 #define True 1
42 #define DEBUG 0
43
44 #define quiet 1
45 #define minpix 10
46 #define fastinfo_flag 0
47
AddData(char * ndata,int nlen)48 void Extension::AddData( char *ndata, int nlen ) {
49 char *hdata=new char[len+nlen];
50 memcpy(hdata,data,len);
51 memcpy(hdata+len,ndata,nlen);
52 delete data;
53 len+=nlen;
54 data=hdata;
55 }
56
GifImage(const char * filename,int autocrop)57 GifImage::GifImage(const char *filename, int autocrop )
58 : lockcount(0) {
59 first=0;
60 LoadGIF( filename );
61 const char *pos = strrchr(filename,'/');
62 if (pos) name=strdup(pos+1);
63 else name=strdup(filename);
64
65 if (autocrop&&!fastinfo_flag) CropImage();
66 }
67
GifImage()68 GifImage::GifImage() {
69 first=0;
70 name=strdup("dummy");
71 lockcount=0;
72 ColorMapSize=128;
73 for (int i=0;i<128;i++) {
74 Red[i] = 255 * ((i>>5)&0x03) / 3;
75 Green[i] = 255 * ((i>>2)&0x07) / 7;
76 Blue[i] = 255 * (i&0x03) / 3;
77 }
78 width =1;
79 height=1;
80 data =(byte*)malloc(1);
81 }
82
~GifImage()83 GifImage::~GifImage() {
84 if (lockcount) {
85 fprintf( stderr, "ERROR: GifImage::~GifImage: image still locked\n" );
86 }
87
88 if (first) delete first;
89
90 free(name);
91 CloseGif();
92 }
93
GetExtensionData(unsigned char code)94 const char *GifImage::GetExtensionData( unsigned char code ) {
95 Extension *current;
96
97 for (current=first;current;current=current->next) {
98 if (current->code==code) return current->data;
99 }
100 return 0;
101 }
102
GetColor(int id,unsigned short * red,unsigned short * green,unsigned short * blue)103 int GifImage::GetColor(int id, unsigned short *red, unsigned short *green, unsigned short *blue) {
104 *red =Red[id] | Red[id]<<8;
105 *green=Green[id] | Green[id]<<8;
106 *blue =Blue[id] | Blue[id]<<8;
107 return 0;
108 }
109
Recolor(class GifImage * gif_p)110 void GifImage::Recolor( class GifImage *gif_p ) {
111 int mapper[256];
112 int i,j;
113
114 for (i=0;i<ColorMapSize;i++) {
115 int min_j = 0;
116 int min_d = 0x7fffffff;
117 for (j=0;j<gif_p->ColorMapSize;j++) {
118 int dr = (gif_p->Red[j] -Red[i]);
119 int dg = (gif_p->Green[j]-Green[i]);
120 int db = (gif_p->Blue[j] -Blue[i]);
121 int d = dr*dr + dg*dg + db*db;
122 if (d<min_d) {
123 min_d = d;
124 min_j = j;
125 }
126 }
127 mapper[i]=min_j;
128 }
129 for (i=0;i<width*height;i++) {
130 data[i] = mapper[data[i]];
131 }
132
133 for (j=0;j<gif_p->ColorMapSize;j++) {
134 Red[j] = gif_p->Red[j];
135 Green[j] = gif_p->Green[j];
136 Blue[j] = gif_p->Blue[j];
137 }
138 ColorMapSize = gif_p->ColorMapSize;
139 }
140
Rotate90()141 void GifImage::Rotate90() {
142 byte *ndata;
143 int help;
144
145 if (!(ndata = (byte *)malloc(width*height))) {
146 fprintf(stderr,"not enough memory to flip image");
147 exit(-1);
148 }
149
150 for (int y=0;y<height;y++) {
151 for (int x=0;x<width;x++) {
152 ndata[(height-y-1)+x*height] = data[x+y*width];
153 }
154 }
155
156 help=width; width=height; height=help;
157 free(data);
158 data=ndata;
159 }
160
AddAt(int x21,int y21,class GifImage * gif_p)161 void GifImage::AddAt( int x21, int y21, class GifImage *gif_p ) {
162 int x22=x21+gif_p->Width();
163 int y22=y21+gif_p->Height();
164
165 int nwidth,nheight;
166 byte *ndata=0;
167
168 if (x21>=0) {
169 if (x22>Width()) nwidth=x22;
170 else nwidth=Width();
171 }
172 else {
173 if (x22>Width()) nwidth=gif_p->Width();
174 else nwidth=(-x21)+Width();
175 }
176 if (y21>=0) {
177 if (y22>Height()) nheight=y22;
178 else nheight=Height();
179 }
180 else {
181 if (y22>Height()) nheight=gif_p->Height();
182 else nheight=(-y21)+Height();
183 }
184
185 if (nwidth>Width()||nheight>Height()) {
186 if (!(ndata = (byte *)malloc(nwidth*nheight))) {
187 fprintf(stderr,"not enough memory for image");
188 exit(-1);
189 }
190
191 /* move first image */
192 memset( ndata, 0, nwidth*nheight );
193 int offx = (x21<0)?-x21:0;
194 int offy = (y21<0)?-y21:0;
195 for (int y=0;y<Height();y++) {
196 memcpy( ndata+offx+(offy+y)*nwidth, data+y*Width(), Width() );
197 }
198 }
199 else ndata=data;
200
201 #if (0)
202 printf( "Colors1: %d\n", ColorMapSize );
203 printf( "Colors2: %d\n", gif_p->ColorMapSize );
204 for (int i=0;i<gif_p->ColorMapSize;i++) {
205 printf( "%3d: %02x %02x %02x %02x %02x %02x\n", i,
206 Red[i], Green[i], Blue[i],
207 gif_p->Red[i], gif_p->Green[i], gif_p->Blue[i] );
208 }
209 #endif
210
211 int offx = (x21>=0)?x21:0;
212 int offy = (y21>=0)?y21:0;
213 for (int y=0;y<gif_p->Height();y++) {
214 memcpy( ndata+offx+(offy+y)*nwidth, gif_p->data+y*gif_p->Width(), gif_p->Width() );
215 }
216
217 if (ndata!=data) {
218 free(data);
219 data = ndata;
220 height = nheight;
221 width = nwidth;
222 }
223 }
224
AddLock(class GifXImage *)225 void GifImage::AddLock(class GifXImage * /*locker*/ ) {
226 lockcount++;
227 }
RemoveLock(class GifXImage *)228 void GifImage::RemoveLock(class GifXImage * /*locker*/ ) {
229 if (!--lockcount) {
230 delete this;
231 }
232 }
233
GetSize(int * w,int * h)234 void GifImage::GetSize( int *w, int *h ) {
235 *w = Width();
236 *h = Height();
237 }
238
GetAverageColor()239 long GifImage::GetAverageColor() {
240 long erg;
241 int i;
242
243 erg=0;
244 for (i=Width()*Height();i>0;i--) {
245 erg+=10*Red[*data];
246 erg+=5*Green[*data];
247 erg+=Blue[*data];
248 data++;
249 }
250 return erg;
251 }
252
253 #define IMAGESEP 0x2c
254 #define EXTENSION 0x21
255 #define INTERLACEMASK 0x40
256 #define COLORMAPMASK 0x80
257 #define ADDTOPIXEL(a) if (Quick) data[Offset++]=a; else AddToPixel(a)
258
259 #define ALLOCATED 3
260
261 static int BitOffset, /* Bit Offset of next code */
262 XC, YC, /* Output X and Y coords of current pixel */
263 Offset, /* Offset in output array */
264 Pass, /* Used by output routine if interlaced pic */
265 BytesPerScanline, /* bytes per scanline in output raster */
266 NumUsed, /* Number of colors really used */
267 CodeSize, /* Code size, read from GIF header */
268 ReadMask; /* Code AND mask for current code size */
269
270 static boolean Interlace;
271
272 static byte *Raster; /* The raster data stream, unblocked */
273
274 /* The GIF headers found in the file */
275 static byte gifheader[13];
276 static byte imageheader[9];
277 static byte colormap[3*256];
278
279 /* The hash table used by the decompressor */
280 static int Prefix[4096];
281 static int Suffix[4096];
282
283 /* An output array used by the decompressor */
284 static byte OutCode[1025];
285
286 /* The color map, read from the GIF header */
287 static byte used[256];
288
289 static char id[] = "GIF";
290
291
292 /*****************************/
LoadGIF(const char * fname)293 int GifImage::LoadGIF(const char *fname)
294 /*****************************/
295 {
296 register byte ch;
297 FILE *fp;
298
299 BitOffset = 0, /* Bit Offset of next code */
300 XC = 0, YC = 0, /* Output X and Y coords of current pixel */
301 Offset = 0, /* Offset in output array */
302 Pass = 0; /* Used by output routine if interlaced pic */
303 ColorMapSize = 0;
304 data = NULL;
305
306 fp = fopen(fname,"rb");
307
308 if (!fp) {
309 fprintf(stderr,"'%s': File not found\n", fname);
310 exit(0);
311 }
312
313 if ( (fread(gifheader, sizeof(gifheader), 1, fp)!=1)
314 || ( (strncmp((char*)gifheader, id, 3)!=0)
315 && (strncmp((char*)gifheader, "FIG", 3)!=0) ) )
316 { fprintf(stderr,"'%s' not a GIF file\n", fname );
317 return 1;
318 }
319
320 if (strncmp((char*)gifheader+3, "87a", 3) && strncmp((char*)gifheader+3,"89a",3))
321 fprintf(stderr,"Warning: %s contains unknown version %c%c%c",fname,
322 gifheader[3],gifheader[4],gifheader[5]);
323 HasColormap = ((gifheader[10] & COLORMAPMASK) ? True : False);
324 ColorMapSize = 1 << (gifheader[10]&7)+1;
325
326 Background = gifheader[11]; /* background color... not used. */
327
328 /* Read in global colormap. */
329 if (HasColormap) ReadColormap(fp);
330
331 /* Check for image extension */
332 while ((ch=getc(fp)) == EXTENSION)
333 {
334 char buffer[256];
335
336 first=new Extension(getc(fp),first);
337 while ((ch=getc(fp))>0) {
338 fread(buffer,ch,1,fp);
339 first->AddData(buffer,ch);
340 }
341 }
342
343 if (ch != IMAGESEP) {
344 fprintf(stderr,"'%s': corrupt GIF file (no image separator) '%x'\n", fname, ch);
345 return 1;
346 }
347
348 fread(imageheader,sizeof(imageheader),1,fp);
349
350 width = imageheader[4] + 0x100 * imageheader[5];
351 height = imageheader[6] + 0x100 * imageheader[7];
352
353 if (!quiet || fastinfo_flag) {
354 printf("%s: %d x %d x %d\n", fname, Width(), Height(), ColorMapSize);
355 fclose(fp);
356 return 1;
357 }
358
359 Interlace = ((imageheader[8] & INTERLACEMASK) ? True : False);
360
361 if (imageheader[8] & COLORMAPMASK)
362 {
363 HasColormap = True;
364 ColorMapSize = 1 << (imageheader[8]&7)+1;
365 ReadColormap(fp);
366 }
367 CodeSize = getc(fp);
368 ReadImageData(fp);
369 fclose(fp);
370 DecodeImage();
371 return 0;
372 }
373
374
ReadImageData(FILE * fp)375 int GifImage::ReadImageData(FILE *fp)
376 {
377 /* Read the raster data. Here we just transpose it from the GIF array
378 * to the Raster array, turning it from a series of blocks into one long
379 * data stream, which makes life much easier for ReadCode().
380 */
381 long filesize, filepos;
382 int ch;
383 byte *ptr1;
384
385 /* find the size of the file */
386 filepos = ftell(fp);
387 fseek(fp, 0L, 2);
388 filesize = ftell(fp)-filepos;
389 fseek(fp, filepos, 0);
390
391 if (!(Raster = (byte *) malloc((unsigned)filesize))) {
392 fprintf(stderr,"Not enough memory to store gif data");
393 exit(-1);
394 }
395
396 ptr1 = Raster;
397 while ((ch = getc(fp))>0)
398 {
399 if (fread(ptr1, 1, ch, fp)<(unsigned)ch)
400 fprintf(stderr,"corrupt GIF file (unblock)\n");
401 ptr1 += ch;
402 }
403 return 0;
404 }
405
CropImage(int x1,int y1,int x2,int y2)406 int GifImage::CropImage(int x1,int y1, int x2, int y2) {
407 int w = x2-x1;
408 int h = y2-y1;
409
410 if (x1<0 || x2>width || w<0 || y1<0 || y2>height || h<0) {
411 fprintf(stderr,"unable to crop (%d,%d)-(%d,%d)\n", x1,y1,x2,y2);
412 fprintf(stderr,"image size: %dx%d\n", width, height );
413 exit(-1);
414 }
415
416 for (int i=0;i<h;i++) {
417 memmove(data+i*w,data+(i+y1)*Width()+x1,w);
418 }
419 width = w;
420 height = h;
421 return 0;
422 }
423
CropImage()424 int GifImage::CropImage() {
425 int i,j;
426 int b;
427 int x1,x2,y1,y2;
428 byte *ptr;
429 long d;
430
431 b=*data;
432 d=0x7fffffff;
433 for (i=0;i<256;i++) {
434 if (Red[i]+Green[i]+Blue[i]<d) {
435 b=i;
436 d=Red[i]+Green[i]+Blue[i];
437 }
438 }
439 x1=-1; x2=Width();
440 y1=-1; y2=Height();
441 for (i=0; i<Height(); i++) {
442 int flag[256];
443 int count=0;
444 ptr = data + (i*Width());
445 for (j=0;j<256;j++) flag[j]=0;
446 for (j=0; j<Width(); j++,ptr++) {
447 if (!flag[*ptr]) {
448 flag[*ptr]++;
449 if (++count>=minpix) break;
450 }
451 }
452 if (count>=minpix) break;
453 y1=i;
454 }
455 for (i=Height()-1;i>=0; i--) {
456 int flag[256];
457 int count=0;
458 ptr = data + (i*Width());
459 for (j=0;j<256;j++) flag[j]=0;
460 for (j=0; j<Width(); j++,ptr++) {
461 if (!flag[*ptr]) {
462 flag[*ptr]++;
463 if (++count>=minpix) break;
464 }
465 }
466 if (count>=minpix) break;
467 y2=i;
468 }
469
470 for (i=0; i<Width(); i++) {
471 int flag[256];
472 int count=0;
473 ptr = data + i;
474 for (j=0;j<256;j++) flag[j]=0;
475 for (j=0; j<Height(); j++,ptr+=Width()) {
476 if (!flag[*ptr]) {
477 flag[*ptr]++;
478 if (++count>=minpix) break;
479 }
480 }
481 if (count>=minpix) break;
482 x1=i;
483 }
484 for (i=Width()-1;i>=0;i--) {
485 int flag[256];
486 int count=0;
487 ptr = data + i;
488 for (j=0;j<256;j++) flag[j]=0;
489 for (j=0; j<Height(); j++,ptr+=Width()) {
490 if (!flag[*ptr]) {
491 flag[*ptr]++;
492 if (++count>=minpix) break;
493 }
494 }
495 if (count>=minpix) break;
496 x2=i;
497 }
498
499 x1++; x2--; y1++; y2--;
500 if (x2<=x1||y2<=y1) return 1;
501
502 CropImage(x1,y1,x2,y2);
503 return 0;
504 }
505
DecodeImage()506 int GifImage::DecodeImage()
507 {
508 /* Start reading the raster data. First we get the intial code size
509 * and compute decompressor constant values, based on this code size.
510 */
511 int Quick, /* True, when not interlaced and local Cmap */
512 InitCodeSize, /* Starting code size, used during Clear */
513 InCode, /* Value returned by ReadCode */
514 MaxCode, /* limiting value for current code size */
515 ClearCode, /* GIF clear code */
516 EOFCode, /* GIF end-of-information code */
517 CurCode, OldCode = 0, /* Decompressor variables */
518 FreeCode, /* Decompressor, next free slot in hashtable */
519 OutCount = 0, /* Decompressor output 'stack count' */
520 FinChar = 0, /* Decompressor variable */
521 BitMask; /* AND mask for data size */
522
523 BitMask = ColorMapSize - 1;
524
525 ClearCode = (1 << CodeSize);
526 EOFCode = ClearCode + 1;
527 FreeCode = ClearCode + 2;
528
529 /* The GIF spec has it that the code size is the code size used to
530 * compute the above values is the code size given in the file, but the
531 * code size used in compression/decompression is the code size given in
532 * the file plus one. (thus the ++).
533 */
534
535 CodeSize++;
536 InitCodeSize = CodeSize;
537 MaxCode = (1 << CodeSize);
538 ReadMask = MaxCode - 1;
539
540 /* Allocate the X Image */
541 if (!(data = (byte *) malloc(Width()*Height()))) {
542 fprintf(stderr,"not enough memory for image");
543 exit(-1);
544 }
545
546 #if (0)
547 if (!(theImage = XCreateImage(theDisp, theVisual, 8, ZPixmap, 0, (char*)Image,
548 Width(), Height(), 8, Width()))) {
549 fprintf(stderr,"unable to create XImage");
550 return -1;
551 }
552 #endif
553
554 BytesPerScanline = Width();
555
556 /* Decompress the file, continuing until you see the GIF EOF code.
557 * One obvious enhancement is to add checking for corrupt files here.
558 */
559 Quick = !Interlace;
560 Offset = 0;
561 if (DEBUG) fprintf(stderr,"Decoding...\n");
562 InCode = ReadCode();
563 while (InCode != EOFCode) {
564
565 /* Clear code sets everything back to its initial value, then reads the
566 * immediately subsequent code as uncompressed data.
567 */
568
569 if (InCode == ClearCode) {
570 CodeSize = InitCodeSize;
571 MaxCode = (1 << CodeSize);
572 ReadMask = MaxCode - 1;
573 FreeCode = ClearCode + 2;
574 CurCode = OldCode = InCode = ReadCode();
575 FinChar = CurCode & BitMask;
576 ADDTOPIXEL(FinChar);
577 }
578 else {
579
580 /* If not a clear code, then must be data: save same as CurCode */
581
582 CurCode = InCode;
583
584 /* If greater or equal to FreeCode, not in the hash table yet;
585 * repeat the last character decoded
586 */
587
588 if (CurCode >= FreeCode) {
589 CurCode = OldCode;
590 OutCode[OutCount++] = FinChar;
591 }
592
593 /* Unless this code is raw data, pursue the chain pointed to by CurCode
594 * through the hash table to its end; each code in the chain puts its
595 * associated output code on the output queue.
596 */
597
598 while (CurCode > BitMask) {
599 if (OutCount >= 1024) {
600 fprintf(stderr,"\nCorrupt GIF file (OutCount)!\n");
601 exit(1);
602 }
603 OutCode[OutCount++] = Suffix[CurCode];
604 CurCode = Prefix[CurCode];
605 }
606
607 /* The last code in the chain is treated as raw data. */
608
609 /* OutCode[OutCount++] = FinChar = CurCode &BitMask*/;
610 FinChar = CurCode & BitMask;
611 ADDTOPIXEL(FinChar);
612
613 /* Now we put the data out to the Output routine.
614 * It's been stacked LIFO, so deal with it that way... */
615 while (OutCount>0)
616 ADDTOPIXEL(OutCode[--OutCount]);
617
618 /* Build the hash table on-the-fly. No table is stored in the file. */
619
620 Prefix[FreeCode] = OldCode;
621 Suffix[FreeCode] = FinChar;
622 OldCode = InCode;
623
624 /* Point to the next slot in the table. If we exceed the current
625 * MaxCode value, increment the code size unless it's already 12. If it
626 * is, do nothing: the next code decompressed better be CLEAR
627 */
628
629 FreeCode++;
630 if (FreeCode >= MaxCode) {
631 if (CodeSize < 12) {
632 CodeSize++;
633 MaxCode *= 2;
634 ReadMask = (1 << CodeSize) - 1;
635 }
636 }
637 }
638 InCode = ReadCode();
639 }
640 free(Raster);
641 return 0;
642 }
643
644
645 /* Fetch the next code from the raster data stream. The codes can be
646 * any length from 3 to 12 bits, packed into 8-bit bytes, so we have to
647 * maintain our location in the Raster array as a BIT Offset. We compute
648 * the byte Offset into the raster array by dividing this by 8, pick up
649 * three bytes, compute the bit Offset into our 24-bit chunk, shift to
650 * bring the desired code to the bottom, then mask it off and return it.
651 */
ReadCode()652 int GifImage::ReadCode()
653 {
654 int RawCode, ByteOffset, BitShift;
655
656 ByteOffset = BitOffset / 8;
657 BitShift = BitOffset % 8;
658 BitOffset += CodeSize;
659 if (BitShift+CodeSize<8)
660 return (Raster[ByteOffset]>>BitShift) & ReadMask;
661 else
662 {
663 RawCode = Raster[ByteOffset] + (0x100 * Raster[ByteOffset + 1]);
664 if (BitShift+CodeSize >= 16)
665 RawCode += (0x10000 * Raster[ByteOffset + 2]);
666 return((RawCode>>BitShift) & ReadMask);
667 }
668 }
669
670
AddToPixel(byte Index)671 void GifImage::AddToPixel(byte Index)
672 {
673 if (YC<Height()) /* Might be of importance when reading interlaced gifs */
674 data[YC*BytesPerScanline+XC] = Index;
675 if (!used[Index]) { used[Index]=True; NumUsed++; }
676 if (++XC == Width())
677 {
678 XC = 0;
679 if (Interlace)
680 {
681 switch (Pass)
682 {
683 case 0: YC += 8; if (YC >= Height()) { Pass++; YC = 4; } break;
684 case 1: YC += 8; if (YC >= Height()) { Pass++; YC = 2; } break;
685 case 2: YC += 4; if (YC >= Height()) { Pass++; YC = 1; } break;
686 case 3: YC += 2; break;
687 default: break;
688 }
689 }
690 else
691 YC++;
692 }
693 }
694
695
696
ReadColormap(FILE * fp)697 void GifImage::ReadColormap(FILE *fp)
698 {
699 byte *ptr=colormap;
700 int i;
701
702 if (DEBUG) fprintf(stderr,"Reading Color map...\n");
703 fread(colormap, ColorMapSize, 3, fp);
704 for (i = 0; i < ColorMapSize; i++) {
705 Red[i] = (*ptr++);
706 Green[i] = (*ptr++);
707 Blue[i] = (*ptr++);
708 used[i] = 0;
709 }
710 NumUsed=0;
711 }
712
713
CloseGif()714 void GifImage::CloseGif()
715 {
716 #if (0)
717 if (LocalCmap)
718 {
719 XFreeColormap(theDisp, LocalCmap);
720 LocalCmap=0;
721 }
722 else
723 {
724 int i,j;
725 unsigned long pixels[256];
726
727 for (j=i=0;i<ColorMapSize;i++)
728 if (used[i]==ALLOCATED)
729 pixels[j++]=cols[i];
730 XFreeColors(theDisp, theCmap, pixels, j, 0L);
731 }
732 #endif
733 if (data)
734 {
735 free(data);
736 data=NULL;
737 }
738 }
739
740