1 //
2 // "$Id: Fl_GIF_Image.cxx 7903 2010-11-28 21:06:39Z matt $"
3 //
4 // Fl_GIF_Image routines.
5 //
6 // Copyright 1997-2010 by Bill Spitzak and others.
7 //
8 // This library is free software; you can redistribute it and/or
9 // modify it under the terms of the GNU Library General Public
10 // License as published by the Free Software Foundation; either
11 // version 2 of the License, or (at your option) any later version.
12 //
13 // This library is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16 // Library General Public License for more details.
17 //
18 // You should have received a copy of the GNU Library General Public
19 // License along with this library; if not, write to the Free Software
20 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
21 // USA.
22 //
23 // Please report all bugs and problems on the following page:
24 //
25 //     http://www.fltk.org/str.php
26 //
27 // Contents:
28 //
29 //
30 
31 //
32 // Include necessary header files...
33 //
34 
35 #include <FL/Fl.H>
36 #include <FL/Fl_GIF_Image.H>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <FL/fl_utf8.h>
40 #include "flstring.h"
41 
42 // Read a .gif file and convert it to a "xpm" format (actually my
43 // modified one with compressed colormaps).
44 
45 // Extensively modified from original code for gif2ras by
46 // Patrick J. Naughton of Sun Microsystems.  The original
47 // copyright notice follows:
48 
49 /* gif2ras.c - Converts from a Compuserve GIF (tm) image to a Sun Raster image.
50  *
51  * Copyright (c) 1988 by Patrick J. Naughton
52  *
53  * Author: Patrick J. Naughton
54  * naughton@wind.sun.com
55  *
56  * Permission to use, copy, modify, and distribute this software and its
57  * documentation for any purpose and without fee is hereby granted,
58  * provided that the above copyright notice appear in all copies and that
59  * both that copyright notice and this permission notice appear in
60  * supporting documentation.
61  *
62  * This file is provided AS IS with no warranties of any kind.  The author
63  * shall have no liability with respect to the infringement of copyrights,
64  * trade secrets or any patents by this file or any part thereof.  In no
65  * event will the author be liable for any lost revenue or profits or
66  * other special, indirect and consequential damages.
67  *
68  * Comments and additions should be sent to the author:
69  *
70  *                     Patrick J. Naughton
71  *                     Sun Microsystems, Inc.
72  *                     2550 Garcia Ave, MS 14-40
73  *                     Mountain View, CA 94043
74  *                     (415) 336-1080
75  */
76 
77 typedef unsigned char uchar;
78 
79 #define NEXTBYTE (uchar)getc(GifFile)
80 #define GETSHORT(var) var = NEXTBYTE; var += NEXTBYTE << 8
81 
82 /**
83   The constructor loads the named GIF image.
84   <P>The inherited destructor free all memory and server resources that are used by
85   the image.
86 */
Fl_GIF_Image(const char * infname)87 Fl_GIF_Image::Fl_GIF_Image(const char *infname) : Fl_Pixmap((char *const*)0) {
88   FILE *GifFile;	// File to read
89   char **new_data;	// Data array
90 
91   if ((GifFile = fl_fopen(infname, "rb")) == NULL) {
92     Fl::error("Fl_GIF_Image: Unable to open %s!", infname);
93     return;
94   }
95 
96   {char b[6];
97   if (fread(b,1,6,GifFile)<6) {
98     fclose(GifFile);
99     return; /* quit on eof */
100   }
101   if (b[0]!='G' || b[1]!='I' || b[2] != 'F') {
102     fclose(GifFile);
103     Fl::error("Fl_GIF_Image: %s is not a GIF file.\n", infname);
104     return;
105   }
106   if (b[3]!='8' || b[4]>'9' || b[5]!= 'a')
107     Fl::warning("%s is version %c%c%c.",infname,b[3],b[4],b[5]);
108   }
109 
110   int Width; GETSHORT(Width);
111   int Height; GETSHORT(Height);
112 
113   uchar ch = NEXTBYTE;
114   char HasColormap = ((ch & 0x80) != 0);
115   int BitsPerPixel = (ch & 7) + 1;
116   int ColorMapSize = 1 << BitsPerPixel;
117   // int OriginalResolution = ((ch>>4)&7)+1;
118   // int SortedTable = (ch&8)!=0;
119   ch = NEXTBYTE; // Background Color index
120   ch = NEXTBYTE; // Aspect ratio is N/64
121 
122   // Read in global colormap:
123   uchar transparent_pixel = 0;
124   char has_transparent = 0;
125   uchar Red[256], Green[256], Blue[256]; /* color map */
126   if (HasColormap) {
127     for (int i=0; i < ColorMapSize; i++) {
128       Red[i] = NEXTBYTE;
129       Green[i] = NEXTBYTE;
130       Blue[i] = NEXTBYTE;
131     }
132   } else {
133     Fl::warning("%s does not have a colormap.", infname);
134     for (int i = 0; i < ColorMapSize; i++)
135       Red[i] = Green[i] = Blue[i] = (uchar)(255 * i / (ColorMapSize-1));
136   }
137 
138   int CodeSize;		/* Code size, init from GIF header, increases... */
139   char Interlace;
140 
141   for (;;) {
142 
143     int i = NEXTBYTE;
144     if (i<0) {
145       fclose(GifFile);
146       Fl::error("Fl_GIF_Image: %s - unexpected EOF",infname);
147       return;
148     }
149     int blocklen;
150 
151     //  if (i == 0x3B) return 0;  eof code
152 
153     if (i == 0x21) {		// a "gif extension"
154 
155       ch = NEXTBYTE;
156       blocklen = NEXTBYTE;
157 
158       if (ch==0xF9 && blocklen==4) { // Netscape animation extension
159 
160 	char bits;
161 	bits = NEXTBYTE;
162 	getc(GifFile); getc(GifFile); // GETSHORT(delay);
163 	transparent_pixel = NEXTBYTE;
164 	if (bits & 1) has_transparent = 1;
165 	blocklen = NEXTBYTE;
166 
167       } else if (ch == 0xFF) { // Netscape repeat count
168 	;
169 
170       } else if (ch != 0xFE) { //Gif Comment
171 	Fl::warning("%s: unknown gif extension 0x%02x.", infname, ch);
172       }
173     } else if (i == 0x2c) {	// an image
174 
175       ch = NEXTBYTE; ch = NEXTBYTE; // GETSHORT(x_position);
176       ch = NEXTBYTE; ch = NEXTBYTE; // GETSHORT(y_position);
177       GETSHORT(Width);
178       GETSHORT(Height);
179       ch = NEXTBYTE;
180       Interlace = ((ch & 0x40) != 0);
181       if (ch&0x80) {
182 	// read local color map
183 	int n = 2<<(ch&7);
184 	for (i=0; i < n; i++) {
185 	  Red[i] = NEXTBYTE;
186 	  Green[i] = NEXTBYTE;
187 	  Blue[i] = NEXTBYTE;
188 	}
189       }
190       CodeSize = NEXTBYTE+1;
191       break; // okay, this is the image we want
192     } else {
193       Fl::warning("%s: unknown gif code 0x%02x", infname, i);
194       blocklen = 0;
195     }
196 
197     // skip the data:
198     while (blocklen>0) {while (blocklen--) {ch = NEXTBYTE;} blocklen=NEXTBYTE;}
199   }
200 
201   if (BitsPerPixel >= CodeSize)
202   {
203     // Workaround for broken GIF files...
204     BitsPerPixel = CodeSize - 1;
205     ColorMapSize = 1 << BitsPerPixel;
206   }
207 
208   uchar *Image = new uchar[Width*Height];
209 
210   int YC = 0, Pass = 0; /* Used to de-interlace the picture */
211   uchar *p = Image;
212   uchar *eol = p+Width;
213 
214   int InitCodeSize = CodeSize;
215   int ClearCode = (1 << (CodeSize-1));
216   int EOFCode = ClearCode + 1;
217   int FirstFree = ClearCode + 2;
218   int FinChar = 0;
219   int ReadMask = (1<<CodeSize) - 1;
220   int FreeCode = FirstFree;
221   int OldCode = ClearCode;
222 
223   // tables used by LZW decompresser:
224   short int Prefix[4096];
225   uchar Suffix[4096];
226 
227   int blocklen = NEXTBYTE;
228   uchar thisbyte = NEXTBYTE; blocklen--;
229   int frombit = 0;
230 
231   for (;;) {
232 
233 /* Fetch the next code from the raster data stream.  The codes can be
234  * any length from 3 to 12 bits, packed into 8-bit bytes, so we have to
235  * maintain our location as a pointer and a bit offset.
236  * In addition, gif adds totally useless and annoying block counts
237  * that must be correctly skipped over. */
238     int CurCode = thisbyte;
239     if (frombit+CodeSize > 7) {
240       if (blocklen <= 0) {
241 	blocklen = NEXTBYTE;
242 	if (blocklen <= 0) break;
243       }
244       thisbyte = NEXTBYTE; blocklen--;
245       CurCode |= thisbyte<<8;
246     }
247     if (frombit+CodeSize > 15) {
248       if (blocklen <= 0) {
249 	blocklen = NEXTBYTE;
250 	if (blocklen <= 0) break;
251       }
252       thisbyte = NEXTBYTE; blocklen--;
253       CurCode |= thisbyte<<16;
254     }
255     CurCode = (CurCode>>frombit)&ReadMask;
256     frombit = (frombit+CodeSize)%8;
257 
258     if (CurCode == ClearCode) {
259       CodeSize = InitCodeSize;
260       ReadMask = (1<<CodeSize) - 1;
261       FreeCode = FirstFree;
262       OldCode = ClearCode;
263       continue;
264     }
265 
266     if (CurCode == EOFCode) break;
267 
268     uchar OutCode[1025]; // temporary array for reversing codes
269     uchar *tp = OutCode;
270     int i;
271     if (CurCode < FreeCode) i = CurCode;
272     else if (CurCode == FreeCode) {*tp++ = (uchar)FinChar; i = OldCode;}
273     else {Fl::error("Fl_GIF_Image: %s - LZW Barf!", infname); break;}
274 
275     while (i >= ColorMapSize) {*tp++ = Suffix[i]; i = Prefix[i];}
276     *tp++ = FinChar = i;
277     do {
278       *p++ = *--tp;
279       if (p >= eol) {
280 	if (!Interlace) YC++;
281 	else switch (Pass) {
282 	case 0: YC += 8; if (YC >= Height) {Pass++; YC = 4;} break;
283 	case 1: YC += 8; if (YC >= Height) {Pass++; YC = 2;} break;
284 	case 2: YC += 4; if (YC >= Height) {Pass++; YC = 1;} break;
285 	case 3: YC += 2; break;
286 	}
287 	if (YC>=Height) YC=0; /* cheap bug fix when excess data */
288 	p = Image + YC*Width;
289 	eol = p+Width;
290       }
291     } while (tp > OutCode);
292 
293     if (OldCode != ClearCode) {
294       Prefix[FreeCode] = (short)OldCode;
295       Suffix[FreeCode] = FinChar;
296       FreeCode++;
297       if (FreeCode > ReadMask) {
298 	if (CodeSize < 12) {
299 	  CodeSize++;
300 	  ReadMask = (1 << CodeSize) - 1;
301 	}
302 	else FreeCode--;
303       }
304     }
305     OldCode = CurCode;
306   }
307 
308   // We are done reading the file, now convert to xpm:
309 
310   // allocate line pointer arrays:
311   w(Width);
312   h(Height);
313   d(1);
314   new_data = new char*[Height+2];
315 
316   // transparent pixel must be zero, swap if it isn't:
317   if (has_transparent && transparent_pixel != 0) {
318     // swap transparent pixel with zero
319     p = Image+Width*Height;
320     while (p-- > Image) {
321       if (*p==transparent_pixel) *p = 0;
322       else if (!*p) *p = transparent_pixel;
323     }
324     uchar t;
325     t                        = Red[0];
326     Red[0]                   = Red[transparent_pixel];
327     Red[transparent_pixel]   = t;
328 
329     t                        = Green[0];
330     Green[0]                 = Green[transparent_pixel];
331     Green[transparent_pixel] = t;
332 
333     t                        = Blue[0];
334     Blue[0]                  = Blue[transparent_pixel];
335     Blue[transparent_pixel]  = t;
336   }
337 
338   // find out what colors are actually used:
339   uchar used[256]; uchar remap[256];
340   int i;
341   for (i = 0; i < ColorMapSize; i++) used[i] = 0;
342   p = Image+Width*Height;
343   while (p-- > Image) used[*p] = 1;
344 
345   // remap them to start with printing characters:
346   int base = has_transparent && used[0] ? ' ' : ' '+1;
347   int numcolors = 0;
348   for (i = 0; i < ColorMapSize; i++) if (used[i]) {
349     remap[i] = (uchar)(base++);
350     numcolors++;
351   }
352 
353   // write the first line of xpm data (use suffix as temp array):
354   int length = sprintf((char*)(Suffix),
355 		       "%d %d %d %d",Width,Height,-numcolors,1);
356   new_data[0] = new char[length+1];
357   strcpy(new_data[0], (char*)Suffix);
358 
359   // write the colormap
360   new_data[1] = (char*)(p = new uchar[4*numcolors]);
361   for (i = 0; i < ColorMapSize; i++) if (used[i]) {
362     *p++ = remap[i];
363     *p++ = Red[i];
364     *p++ = Green[i];
365     *p++ = Blue[i];
366   }
367 
368   // remap the image data:
369   p = Image+Width*Height;
370   while (p-- > Image) *p = remap[*p];
371 
372   // split the image data into lines:
373   for (i=0; i<Height; i++) {
374     new_data[i+2] = new char[Width+1];
375     memcpy(new_data[i + 2], (char*)(Image + i*Width), Width);
376     new_data[i + 2][Width] = 0;
377   }
378 
379   data((const char **)new_data, Height + 2);
380   alloc_data = 1;
381 
382   delete[] Image;
383 
384   fclose(GifFile);
385 }
386 
387 
388 //
389 // End of "$Id: Fl_GIF_Image.cxx 7903 2010-11-28 21:06:39Z matt $".
390 //
391