diff options
| author | Matthias Melcher <git@matthiasm.com> | 2020-01-03 21:37:52 +0100 |
|---|---|---|
| committer | Matthias Melcher <git@matthiasm.com> | 2020-01-03 21:37:52 +0100 |
| commit | 1ba9e64ba9fad8bb0193239e53878d471b2f4d53 (patch) | |
| tree | 5f3f498d443c4cec6a13b23ed24fba007138c388 | |
| parent | 86893a90cba24137d3788a07afab22e74ad3d7ab (diff) | |
Added code to read GIF files from memory (GitHub issue #33, 2/2)
| -rw-r--r-- | FL/Fl_GIF_Image.H | 8 | ||||
| -rw-r--r-- | src/Fl_GIF_Image.cxx | 271 |
2 files changed, 188 insertions, 91 deletions
diff --git a/FL/Fl_GIF_Image.H b/FL/Fl_GIF_Image.H index a24079a8e..096104a09 100644 --- a/FL/Fl_GIF_Image.H +++ b/FL/Fl_GIF_Image.H @@ -30,9 +30,15 @@ */ class FL_EXPORT Fl_GIF_Image : public Fl_Pixmap { - public: +public: Fl_GIF_Image(const char* filename); + Fl_GIF_Image(const char* filename, const unsigned char *data); + +protected: + + void read(class GIFReader &rdr); + }; #endif diff --git a/src/Fl_GIF_Image.cxx b/src/Fl_GIF_Image.cxx index 97adb23ce..aba6cf01a 100644 --- a/src/Fl_GIF_Image.cxx +++ b/src/Fl_GIF_Image.cxx @@ -77,8 +77,80 @@ typedef unsigned char uchar; -#define NEXTBYTE (uchar)getc(GifFile) -#define GETSHORT(var) var = NEXTBYTE; var += NEXTBYTE << 8 +// +// Local reader class... +// + +class GIFReader +{ +public: + GIFReader() : + pIsFile(0), pIsData(0), + pFile(0L), pData(0L), pStart(0L), + pName(0L) + { } + int open(const char *filename) { + if (filename) + pName = strdup(filename); + if ((pFile = fl_fopen(filename, "rb")) == NULL) { + return -1; + } else { + pIsFile = 1; + return 0; + } + } + int open(const char *imagename, const unsigned char *data) { + if (imagename) + pName = strdup(imagename); + if (data) { + pStart = pData = data; + pIsData = 1; + return 0; + } else { + return -1; + } + } + ~GIFReader() { + if (pIsFile && pFile) { + fclose(pFile); + } + if (pName) + ::free(pName); + } + uchar read_byte() { + if (pIsFile) { + return getc(pFile); + } else if (pIsData) { + return *pData++; + } else { + return 0; + } + } + // 'read_word()' - Read a 16-bit unsigned integer. + unsigned short read_word() { + unsigned char b0, b1; // Bytes from file + if (pIsFile) { + b0 = (uchar)getc(pFile); + b1 = (uchar)getc(pFile); + return ((b1 << 8) | b0); + } else if (pIsData) { + b0 = *pData++; + b1 = *pData++; + return ((b1 << 8) | b0); + } else { + return 0; + } + } + const char *name() { return pName; } +private: + char pIsFile; + char pIsData; + FILE *pFile; + const unsigned char *pData; + const unsigned char *pStart; + char *pName; +}; + /** The constructor loads the named GIF image. @@ -91,36 +163,58 @@ typedef unsigned char uchar; 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_GIF_Image::Fl_GIF_Image(const char *infname) : + Fl_Pixmap((char *const*)0) +{ + GIFReader f; + if (f.open(infname)==-1) { Fl::error("Fl_GIF_Image: Unable to open %s!", infname); ld(ERR_FILE_ACCESS); - return; + } else { + read(f); } +} - {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); + +/** + The constructor loads the named GIF image. + + \param[in] bmp the name of the bitmap + \param[in] data a pointer to the BMP data in memory. There is no checking for buffer overruns + + \see Fl_BMP_Image::Fl_BMP_Image(const char *bmp) + */ +Fl_GIF_Image::Fl_GIF_Image(const char *gif, const unsigned char *data) : + Fl_Pixmap((char *const*)0) +{ + GIFReader d; + if (d.open(gif, data)==-1) { ld(ERR_FORMAT); - return; + } else { + read(d); } - 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]); +} + + +void Fl_GIF_Image::read(GIFReader &rdr) +{ + char **new_data; // Data array + + {char b[6] = { 0 }; + for (int i=0; i<6; ++i) b[i] = rdr.read_byte(); + if (b[0]!='G' || b[1]!='I' || b[2] != 'F') { + Fl::error("Fl_GIF_Image: %s is not a GIF file.\n", rdr.name()); + ld(ERR_FORMAT); + return; + } + if (b[3]!='8' || b[4]>'9' || b[5]!= 'a') + Fl::warning("%s is version %c%c%c.",rdr.name(),b[3],b[4],b[5]); } - int Width; GETSHORT(Width); - int Height; GETSHORT(Height); + int Width = rdr.read_word(); + int Height = rdr.read_word(); - uchar ch = NEXTBYTE; + uchar ch = rdr.read_byte(); char HasColormap = ((ch & 0x80) != 0); int BitsPerPixel = (ch & 7) + 1; int ColorMapSize; @@ -131,18 +225,18 @@ Fl_GIF_Image::Fl_GIF_Image(const char *infname) : Fl_Pixmap((char *const*)0) { } // int OriginalResolution = ((ch>>4)&7)+1; // int SortedTable = (ch&8)!=0; - ch = NEXTBYTE; // Background Color index - ch = NEXTBYTE; // Aspect ratio is N/64 + ch = rdr.read_byte(); // Background Color index + ch = rdr.read_byte(); // 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; + for (int i=0; i < ColorMapSize; i++) { + Red[i] = rdr.read_byte(); + Green[i] = rdr.read_byte(); + Blue[i] = rdr.read_byte(); } } @@ -151,10 +245,9 @@ Fl_GIF_Image::Fl_GIF_Image(const char *infname) : Fl_Pixmap((char *const*)0) { for (;;) { - int i = NEXTBYTE; + int i = rdr.read_byte(); if (i<0) { - fclose(GifFile); - Fl::error("Fl_GIF_Image: %s - unexpected EOF",infname); + Fl::error("Fl_GIF_Image: %s - unexpected EOF", rdr.name()); w(0); h(0); d(0); ld(ERR_FORMAT); return; } @@ -164,50 +257,50 @@ Fl_GIF_Image::Fl_GIF_Image(const char *infname) : Fl_Pixmap((char *const*)0) { if (i == 0x21) { // a "gif extension" - ch = NEXTBYTE; - blocklen = NEXTBYTE; + ch = rdr.read_byte(); + blocklen = rdr.read_byte(); 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; + char bits; + bits = rdr.read_byte(); + rdr.read_word(); // GETSHORT(delay); + transparent_pixel = rdr.read_byte(); + if (bits & 1) has_transparent = 1; + blocklen = rdr.read_byte(); } else if (ch == 0xFF) { // Netscape repeat count - ; + ; } else if (ch != 0xFE) { //Gif Comment - Fl::warning("%s: unknown gif extension 0x%02x.", infname, ch); + Fl::warning("%s: unknown gif extension 0x%02x.", rdr.name(), 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; + ch = rdr.read_byte(); ch = rdr.read_byte(); // GETSHORT(x_position); + ch = rdr.read_byte(); ch = rdr.read_byte(); // GETSHORT(y_position); + Width = rdr.read_word(); + Height = rdr.read_word(); + ch = rdr.read_byte(); Interlace = ((ch & 0x40) != 0); if (ch & 0x80) { // image has local color table - BitsPerPixel = (ch & 7) + 1; - ColorMapSize = 2 << (ch & 7); - for (i=0; i < ColorMapSize; i++) { - Red[i] = NEXTBYTE; - Green[i] = NEXTBYTE; - Blue[i] = NEXTBYTE; - } + BitsPerPixel = (ch & 7) + 1; + ColorMapSize = 2 << (ch & 7); + for (i=0; i < ColorMapSize; i++) { + Red[i] = rdr.read_byte(); + Green[i] = rdr.read_byte(); + Blue[i] = rdr.read_byte(); + } } - CodeSize = NEXTBYTE+1; + CodeSize = rdr.read_byte()+1; break; // okay, this is the image we want } else { - Fl::warning("%s: unknown gif code 0x%02x", infname, i); + Fl::warning("%s: unknown gif code 0x%02x", rdr.name(), i); blocklen = 0; } // skip the data: - while (blocklen>0) {while (blocklen--) {ch = NEXTBYTE;} blocklen=NEXTBYTE;} + while (blocklen>0) {while (blocklen--) {ch = rdr.read_byte();} blocklen=rdr.read_byte();} } if (BitsPerPixel >= CodeSize) @@ -222,7 +315,7 @@ Fl_GIF_Image::Fl_GIF_Image(const char *infname) : Fl_Pixmap((char *const*)0) { // first two color table entries should be black and white. if (ColorMapSize == 0) { // no global and no local color table - Fl::warning("%s does not have a color table, using default.\n", infname); + Fl::warning("%s does not have a color table, using default.\n", rdr.name()); BitsPerPixel = CodeSize - 1; ColorMapSize = 1 << BitsPerPixel; Red[0] = Green[0] = Blue[0] = 0; // black @@ -257,32 +350,32 @@ Fl_GIF_Image::Fl_GIF_Image(const char *infname) : Fl_Pixmap((char *const*)0) { short int Prefix[4096]; uchar Suffix[4096]; - int blocklen = NEXTBYTE; - uchar thisbyte = NEXTBYTE; blocklen--; + int blocklen = rdr.read_byte(); + uchar thisbyte = rdr.read_byte(); blocklen--; int frombit = 0; for (;;) { -/* Fetch the next code from the raster data stream. The codes can be - * any length from 3 to 12 bits, packed into 8-bit bytes, so we have to - * maintain our location as a pointer and a bit offset. - * In addition, gif adds totally useless and annoying block counts - * that must be correctly skipped over. */ + /* Fetch the next code from the raster data stream. The codes can be + * any length from 3 to 12 bits, packed into 8-bit bytes, so we have to + * maintain our location as a pointer and a bit offset. + * In addition, gif adds totally useless and annoying block counts + * that must be correctly skipped over. */ int CurCode = thisbyte; if (frombit+CodeSize > 7) { if (blocklen <= 0) { - blocklen = NEXTBYTE; - if (blocklen <= 0) break; + blocklen = rdr.read_byte(); + if (blocklen <= 0) break; } - thisbyte = NEXTBYTE; blocklen--; + thisbyte = rdr.read_byte(); blocklen--; CurCode |= thisbyte<<8; } if (frombit+CodeSize > 15) { if (blocklen <= 0) { - blocklen = NEXTBYTE; - if (blocklen <= 0) break; + blocklen = rdr.read_byte(); + if (blocklen <= 0) break; } - thisbyte = NEXTBYTE; blocklen--; + thisbyte = rdr.read_byte(); blocklen--; CurCode |= thisbyte<<16; } CurCode = (CurCode>>frombit)&ReadMask; @@ -303,23 +396,23 @@ Fl_GIF_Image::Fl_GIF_Image(const char *infname) : Fl_Pixmap((char *const*)0) { int i; if (CurCode < FreeCode) i = CurCode; else if (CurCode == FreeCode) {*tp++ = (uchar)FinChar; i = OldCode;} - else {Fl::error("Fl_GIF_Image: %s - LZW Barf!", infname); break;} + else {Fl::error("Fl_GIF_Image: %s - LZW Barf!", rdr.name()); break;} while (i >= 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; + 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); @@ -328,11 +421,11 @@ Fl_GIF_Image::Fl_GIF_Image(const char *infname) : Fl_Pixmap((char *const*)0) { Suffix[FreeCode] = FinChar; FreeCode++; if (FreeCode > ReadMask) { - if (CodeSize < 12) { - CodeSize++; - ReadMask = (1 << CodeSize) - 1; - } - else FreeCode--; + if (CodeSize < 12) { + CodeSize++; + ReadMask = (1 << CodeSize) - 1; + } + else FreeCode--; } } OldCode = CurCode; @@ -385,7 +478,7 @@ Fl_GIF_Image::Fl_GIF_Image(const char *infname) : Fl_Pixmap((char *const*)0) { // 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); + "%d %d %d %d",Width,Height,-numcolors,1); new_data[0] = new char[length+1]; strcpy(new_data[0], (char*)Suffix); @@ -413,8 +506,6 @@ Fl_GIF_Image::Fl_GIF_Image(const char *infname) : Fl_Pixmap((char *const*)0) { alloc_data = 1; delete[] Image; - - fclose(GifFile); } |
