// // "$Id: Fl_GIF_Image.cxx 10751 2015-06-14 17:07:31Z AlbrechtS $" // // Fl_GIF_Image routines. // // Copyright 1997-2015 by Bill Spitzak and others. // // This library is free software. Distribution and use rights are outlined in // the file "COPYING" which should have been included with this file. If this // file is missing or damaged, see the license at: // // http://www.fltk.org/COPYING.php // // Please report all bugs and problems on the following page: // // http://www.fltk.org/str.php // // Contents: // // // // Include necessary header files... // #include #include #include #include #include #include "flstring.h" // Read a .gif file and convert it to a "xpm" format (actually my // modified one with compressed colormaps). // Extensively modified from original code for gif2ras by // Patrick J. Naughton of Sun Microsystems. The original // copyright notice follows: /* gif2ras.c - Converts from a Compuserve GIF (tm) image to a Sun Raster image. * * Copyright (c) 1988 by Patrick J. Naughton * * Author: Patrick J. Naughton * naughton@wind.sun.com * * Permission to use, copy, modify, and distribute this software and its * documentation for any purpose and without fee is hereby granted, * provided that the above copyright notice appear in all copies and that * both that copyright notice and this permission notice appear in * supporting documentation. * * This file is provided AS IS with no warranties of any kind. The author * shall have no liability with respect to the infringement of copyrights, * trade secrets or any patents by this file or any part thereof. In no * event will the author be liable for any lost revenue or profits or * other special, indirect and consequential damages. * * Comments and additions should be sent to the author: * * Patrick J. Naughton * Sun Microsystems, Inc. * 2550 Garcia Ave, MS 14-40 * Mountain View, CA 94043 * (415) 336-1080 */ typedef unsigned char uchar; #define NEXTBYTE (uchar)getc(GifFile) #define GETSHORT(var) var = NEXTBYTE; var += NEXTBYTE << 8 /** The constructor loads the named GIF image. The destructor frees all memory and server resources that are used by the image. Use Fl_Image::fail() to check if Fl_GIF_Image failed to load. fail() returns ERR_FILE_ACCESS if the file could not be opened or read, ERR_FORMAT if the GIF format could not be decoded, and ERR_NO_IMAGE if the image could not be loaded for another reason. */ Fl_GIF_Image::Fl_GIF_Image(const char *infname) : Fl_Pixmap((char *const*)0) { FILE *GifFile; // File to read char **new_data; // Data array if ((GifFile = fl_fopen(infname, "rb")) == NULL) { Fl::error("Fl_GIF_Image: Unable to open %s!", infname); ld(ERR_FILE_ACCESS); return; } {char b[6]; if (fread(b,1,6,GifFile)<6) { fclose(GifFile); ld(ERR_FILE_ACCESS); return; /* quit on eof */ } if (b[0]!='G' || b[1]!='I' || b[2] != 'F') { fclose(GifFile); Fl::error("Fl_GIF_Image: %s is not a GIF file.\n", infname); ld(ERR_FORMAT); return; } if (b[3]!='8' || b[4]>'9' || b[5]!= 'a') Fl::warning("%s is version %c%c%c.",infname,b[3],b[4],b[5]); } int Width; GETSHORT(Width); int Height; GETSHORT(Height); uchar ch = NEXTBYTE; char HasColormap = ((ch & 0x80) != 0); int BitsPerPixel = (ch & 7) + 1; int ColorMapSize = 1 << BitsPerPixel; // int OriginalResolution = ((ch>>4)&7)+1; // int SortedTable = (ch&8)!=0; ch = NEXTBYTE; // Background Color index ch = NEXTBYTE; // Aspect ratio is N/64 // Read in global colormap: uchar transparent_pixel = 0; char has_transparent = 0; uchar Red[256], Green[256], Blue[256]; /* color map */ if (HasColormap) { for (int i=0; i < ColorMapSize; i++) { Red[i] = NEXTBYTE; Green[i] = NEXTBYTE; Blue[i] = NEXTBYTE; } } else { Fl::warning("%s does not have a colormap.", infname); for (int i = 0; i < ColorMapSize; i++) Red[i] = Green[i] = Blue[i] = (uchar)(255 * i / (ColorMapSize-1)); } int CodeSize; /* Code size, init from GIF header, increases... */ char Interlace; for (;;) { int i = NEXTBYTE; if (i<0) { fclose(GifFile); Fl::error("Fl_GIF_Image: %s - unexpected EOF",infname); w(0); h(0); d(0); ld(ERR_FORMAT); return; } int blocklen; // if (i == 0x3B) return 0; eof code if (i == 0x21) { // a "gif extension" ch = NEXTBYTE; blocklen = NEXTBYTE; if (ch==0xF9 && blocklen==4) { // Netscape animation extension char bits; bits = NEXTBYTE; getc(GifFile); getc(GifFile); // GETSHORT(delay); transparent_pixel = NEXTBYTE; if (bits & 1) has_transparent = 1; blocklen = NEXTBYTE; } else if (ch == 0xFF) { // Netscape repeat count ; } else if (ch != 0xFE) { //Gif Comment Fl::warning("%s: unknown gif extension 0x%02x.", infname, ch); } } else if (i == 0x2c) { // an image ch = NEXTBYTE; ch = NEXTBYTE; // GETSHORT(x_position); ch = NEXTBYTE; ch = NEXTBYTE; // GETSHORT(y_position); GETSHORT(Width); GETSHORT(Height); ch = NEXTBYTE; Interlace = ((ch & 0x40) != 0); if (ch&0x80) { // read local color map int n = 2<<(ch&7); for (i=0; i < n; i++) { Red[i] = NEXTBYTE; Green[i] = NEXTBYTE; Blue[i] = NEXTBYTE; } } CodeSize = NEXTBYTE+1; break; // okay, this is the image we want } else { Fl::warning("%s: unknown gif code 0x%02x", infname, i); blocklen = 0; } // skip the data: while (blocklen>0) {while (blocklen--) {ch = NEXTBYTE;} blocklen=NEXTBYTE;} } if (BitsPerPixel >= CodeSize) { // Workaround for broken GIF files... BitsPerPixel = CodeSize - 1; ColorMapSize = 1 << BitsPerPixel; } uchar *Image = new uchar[Width*Height]; int YC = 0, Pass = 0; /* Used to de-interlace the picture */ uchar *p = Image; uchar *eol = p+Width; int InitCodeSize = CodeSize; int ClearCode = (1 << (CodeSize-1)); int EOFCode = ClearCode + 1; int FirstFree = ClearCode + 2; int FinChar = 0; int ReadMask = (1< 7) { if (blocklen <= 0) { blocklen = NEXTBYTE; if (blocklen <= 0) break; } thisbyte = NEXTBYTE; blocklen--; CurCode |= thisbyte<<8; } if (frombit+CodeSize > 15) { if (blocklen <= 0) { blocklen = NEXTBYTE; if (blocklen <= 0) break; } thisbyte = NEXTBYTE; blocklen--; CurCode |= thisbyte<<16; } CurCode = (CurCode>>frombit)&ReadMask; frombit = (frombit+CodeSize)%8; if (CurCode == ClearCode) { CodeSize = InitCodeSize; ReadMask = (1<= ColorMapSize) {*tp++ = Suffix[i]; i = Prefix[i];} *tp++ = FinChar = i; do { *p++ = *--tp; if (p >= eol) { if (!Interlace) YC++; else switch (Pass) { case 0: YC += 8; if (YC >= Height) {Pass++; YC = 4;} break; case 1: YC += 8; if (YC >= Height) {Pass++; YC = 2;} break; case 2: YC += 4; if (YC >= Height) {Pass++; YC = 1;} break; case 3: YC += 2; break; } if (YC>=Height) YC=0; /* cheap bug fix when excess data */ p = Image + YC*Width; eol = p+Width; } } while (tp > OutCode); if (OldCode != ClearCode) { Prefix[FreeCode] = (short)OldCode; Suffix[FreeCode] = FinChar; FreeCode++; if (FreeCode > ReadMask) { if (CodeSize < 12) { CodeSize++; ReadMask = (1 << CodeSize) - 1; } else FreeCode--; } } OldCode = CurCode; } // We are done reading the file, now convert to xpm: // allocate line pointer arrays: w(Width); h(Height); d(1); new_data = new char*[Height+2]; // transparent pixel must be zero, swap if it isn't: if (has_transparent && transparent_pixel != 0) { // swap transparent pixel with zero p = Image+Width*Height; while (p-- > Image) { if (*p==transparent_pixel) *p = 0; else if (!*p) *p = transparent_pixel; } uchar t; t = Red[0]; Red[0] = Red[transparent_pixel]; Red[transparent_pixel] = t; t = Green[0]; Green[0] = Green[transparent_pixel]; Green[transparent_pixel] = t; t = Blue[0]; Blue[0] = Blue[transparent_pixel]; Blue[transparent_pixel] = t; } // find out what colors are actually used: uchar used[256]; uchar remap[256]; int i; for (i = 0; i < ColorMapSize; i++) used[i] = 0; p = Image+Width*Height; while (p-- > Image) used[*p] = 1; // remap them to start with printing characters: int base = has_transparent && used[0] ? ' ' : ' '+1; int numcolors = 0; for (i = 0; i < ColorMapSize; i++) if (used[i]) { remap[i] = (uchar)(base++); numcolors++; } // write the first line of xpm data (use suffix as temp array): int length = sprintf((char*)(Suffix), "%d %d %d %d",Width,Height,-numcolors,1); new_data[0] = new char[length+1]; strcpy(new_data[0], (char*)Suffix); // write the colormap new_data[1] = (char*)(p = new uchar[4*numcolors]); for (i = 0; i < ColorMapSize; i++) if (used[i]) { *p++ = remap[i]; *p++ = Red[i]; *p++ = Green[i]; *p++ = Blue[i]; } // remap the image data: p = Image+Width*Height; while (p-- > Image) *p = remap[*p]; // split the image data into lines: for (i=0; i