diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/Fl_GIF_Image.cxx | 262 | ||||
| -rw-r--r-- | src/Fl_Image_Reader.h | 3 |
2 files changed, 178 insertions, 87 deletions
diff --git a/src/Fl_GIF_Image.cxx b/src/Fl_GIF_Image.cxx index d747ac480..32a1dabd9 100644 --- a/src/Fl_GIF_Image.cxx +++ b/src/Fl_GIF_Image.cxx @@ -1,7 +1,7 @@ // // Fl_GIF_Image routines. // -// Copyright 1997-2020 by Bill Spitzak and others. +// Copyright 1997-2021 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 @@ -74,23 +74,23 @@ /** - \brief The constructor loads the named GIF image. + This constructor loads a GIF image from the given file. - IF a GIF is animated, Fl_GIF_Image will only read and display the first frame - of the animation. + IF a GIF image is animated, Fl_GIF_Image will only read and display the + first frame of the animation. - The destructor frees all memory and server resources that are used by - the 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. + 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. - \param[in] filename a full path and name pointing to a valid GIF file. + \param[in] filename a full path and name pointing to a GIF image file. - \see Fl_GIF_Image::Fl_GIF_Image(const char *imagename, const unsigned char *data) - */ + \see Fl_GIF_Image::Fl_GIF_Image(const char *imagename, const unsigned char *data, const long length) +*/ Fl_GIF_Image::Fl_GIF_Image(const char *filename) : Fl_Pixmap((char *const*)0) { @@ -105,32 +105,47 @@ Fl_GIF_Image::Fl_GIF_Image(const char *filename) : /** - \brief The constructor loads a GIF image from memory. + This constructor loads a GIF image from memory. + + Construct an image from a block of memory inside the application. Fluid offers + "binary data" chunks as a great way to add image data into the C++ source code. + \p imagename can be NULL. If a name is given, the image is added to the list of + shared images and will be available by that name. + + IF a GIF image is animated, Fl_GIF_Image will only read and display the + first frame of the animation. + + The destructor frees all memory and server resources that are used by + the image. + + The (new and optional) third parameter \p length \b should be used so buffer + overruns (i.e. truncated images) can be checked. See note below. - Construct an image from a block of memory inside the application. Fluid offers - "binary Data" chunks as a great way to add image data into the C++ source code. - imagename can be NULL. If a name is given, the image is added to the list of - shared images and will be available by that name. + If \p length is not used + - it defaults to -1 (unlimited size) + - buffer overruns will not be checked. - IF a GIF is animated, Fl_GIF_Image will only read and display the first frame - of the animation. + \note The optional parameter \p length is available since FLTK 1.4.0. + Not using it is deprecated and old code should be modified to use it. + This parameter will likely become mandatory in a future FLTK version. - 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. + 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. - \param[in] imagename A name given to this image or NULL - \param[in] data Pointer to the start of the GIF image in memory. This code will not check for buffer overruns. + \param[in] imagename A name given to this image or NULL + \param[in] data Pointer to the start of the GIF image in memory. + \param[in] length Length of the GIF image in memory. - \see Fl_GIF_Image::Fl_GIF_Image(const char *filename) - \see Fl_Shared_Image + \see Fl_GIF_Image::Fl_GIF_Image(const char *filename) + \see Fl_Shared_Image */ -Fl_GIF_Image::Fl_GIF_Image(const char *imagename, const unsigned char *data) : +Fl_GIF_Image::Fl_GIF_Image(const char *imagename, const unsigned char *data, const long length) : Fl_Pixmap((char *const*)0) { Fl_Image_Reader rdr; - if (rdr.open(imagename, data)==-1) { + if (rdr.open(imagename, data, length) == -1) { ld(ERR_FILE_ACCESS); } else { load_gif_(rdr); @@ -138,6 +153,19 @@ Fl_GIF_Image::Fl_GIF_Image(const char *imagename, const unsigned char *data) : } /* + This macro can be used to check for end of file (EOF) or other read errors. + In case of an error or EOF an error message is issued and the image loading + is terminated with error code ERR_FORMAT. +*/ +#define CHECK_ERROR \ + if (rdr.error()) { \ + Fl::error("[%d] Fl_GIF_Image: %s - unexpected EOF or read error at offset %ld", \ + __LINE__, rdr.name(), rdr.tell()); \ + ld(ERR_FORMAT); \ + return; \ + } + +/* This method reads GIF image data and creates an RGB or RGBA image. The GIF format supports only 1 bit for alpha. To avoid code duplication, we use an Fl_Image_Reader that reads data from either a file or from memory. @@ -145,6 +173,9 @@ Fl_GIF_Image::Fl_GIF_Image(const char *imagename, const unsigned char *data) : void Fl_GIF_Image::load_gif_(Fl_Image_Reader &rdr) { char **new_data; // Data array + w(0); h(0); + + // printf("\nFl_GIF_Image::load_gif_ : %s\n", rdr.name()); {char b[6] = { 0 }; for (int i=0; i<6; ++i) b[i] = rdr.read_byte(); @@ -161,6 +192,7 @@ void Fl_GIF_Image::load_gif_(Fl_Image_Reader &rdr) int Height = rdr.read_word(); uchar ch = rdr.read_byte(); + CHECK_ERROR char HasColormap = ((ch & 0x80) != 0); int BitsPerPixel = (ch & 7) + 1; int ColorMapSize; @@ -173,6 +205,7 @@ void Fl_GIF_Image::load_gif_(Fl_Image_Reader &rdr) // int SortedTable = (ch&8)!=0; ch = rdr.read_byte(); // Background Color index ch = rdr.read_byte(); // Aspect ratio is N/64 + CHECK_ERROR // Read in global colormap: uchar transparent_pixel = 0; @@ -185,6 +218,7 @@ void Fl_GIF_Image::load_gif_(Fl_Image_Reader &rdr) Blue[i] = rdr.read_byte(); } } + CHECK_ERROR int CodeSize; /* Code size, init from GIF header, increases... */ char Interlace; @@ -192,44 +226,52 @@ void Fl_GIF_Image::load_gif_(Fl_Image_Reader &rdr) for (;;) { int i = rdr.read_byte(); - if (i<0) { - Fl::error("Fl_GIF_Image: %s - unexpected EOF", rdr.name()); - w(0); h(0); d(0); ld(ERR_FORMAT); - return; - } + CHECK_ERROR int blocklen; - // if (i == 0x3B) return 0; eof code - - if (i == 0x21) { // a "gif extension" - - ch = rdr.read_byte(); + if (i == 0x21) { // a "gif extension" + ch = rdr.read_byte(); // extension type blocklen = rdr.read_byte(); - - if (ch==0xF9 && blocklen==4) { // Netscape animation extension - - char bits; - bits = rdr.read_byte(); - rdr.read_word(); // GETSHORT(delay); - transparent_pixel = rdr.read_byte(); + CHECK_ERROR + + if (ch == 0xF9 && blocklen == 4) { // Graphic Control Extension + // printf("Graphic Control Extension at offset %ld\n", rdr.tell()-2); + char bits = rdr.read_byte(); // Packed Fields + rdr.read_word(); // Delay Time + transparent_pixel = rdr.read_byte(); // Transparent Color Index + blocklen = rdr.read_byte(); // Block Terminator (must be zero) + CHECK_ERROR 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.", rdr.name(), ch); } - } else if (i == 0x2c) { // an image - - 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(); + else if (ch == 0xFF) { // Application Extension + // printf("Application Extension at offset %ld, length = %d\n", rdr.tell()-3, blocklen); + ; // skip data + } + else if (ch == 0xFE) { // Comment Extension + // printf("Comment Extension at offset %ld, length = %d\n", rdr.tell()-3, blocklen); + ; // skip data + } + else if (ch == 0x01) { // Plain Text Extension + // printf("Plain Text Extension at offset %ld, length = %d\n", rdr.tell()-3, blocklen); + ; // skip data + } + else { + Fl::warning("%s: unknown GIF extension 0x%02x at offset %ld, length = %d", + rdr.name(), ch, rdr.tell()-3, blocklen); + ; // skip data + } + } + else if (i == 0x2c) { // an image: Image Descriptor follows + // printf("Image Descriptor at offset %ld\n", rdr.tell()); + rdr.read_word(); // Image Left Position + rdr.read_word(); // Image Top Position + Width = rdr.read_word(); // Image Width + Height = rdr.read_word(); // Image Height + ch = rdr.read_byte(); // Packed Fields + CHECK_ERROR Interlace = ((ch & 0x40) != 0); - if (ch & 0x80) { // image has local color table + if (ch & 0x80) { // image has local color table + // printf("Local Color Table at offset %ld\n", rdr.tell()); BitsPerPixel = (ch & 7) + 1; ColorMapSize = 2 << (ch & 7); for (i=0; i < ColorMapSize; i++) { @@ -238,20 +280,30 @@ void Fl_GIF_Image::load_gif_(Fl_Image_Reader &rdr) Blue[i] = rdr.read_byte(); } } - CodeSize = rdr.read_byte()+1; + CHECK_ERROR break; // okay, this is the image we want } else { - Fl::warning("%s: unknown gif code 0x%02x", rdr.name(), i); + Fl::warning("%s: unknown GIF code 0x%02x at offset %ld", rdr.name(), i, rdr.tell()-1); blocklen = 0; } + CHECK_ERROR - // skip the data: - while (blocklen>0) {while (blocklen--) {ch = rdr.read_byte();} blocklen = rdr.read_byte();} + // skip all the data subblocks: + while (blocklen > 0) { + rdr.skip(blocklen); + blocklen = rdr.read_byte(); + } + // printf("End of data at offset %ld\n", rdr.tell()); } - if (BitsPerPixel >= CodeSize) - { - // Workaround for broken GIF files... + // read image data + + // printf("Image Data at offset %ld\n", rdr.tell()); + + CodeSize = rdr.read_byte() + 1; // LZW Minimum Code Size + CHECK_ERROR + + if (BitsPerPixel >= CodeSize) { // Workaround for broken GIF files... BitsPerPixel = CodeSize - 1; ColorMapSize = 1 << BitsPerPixel; } @@ -269,14 +321,25 @@ void Fl_GIF_Image::load_gif_(Fl_Image_Reader &rdr) for (int i = 2; i < ColorMapSize; i++) { Red[i] = Green[i] = Blue[i] = (uchar)(255 * i / (ColorMapSize - 1)); } -#if (0) - // fill color table to maximum size - for (int i = ColorMapSize; i < 256; i++) { - Red[i] = Green[i] = Blue[i] = 0; // black - } -#endif } + // Fix transparent pixel index outside ColorMap (Issue #271) + if (has_transparent && transparent_pixel >= ColorMapSize) { + for (int k = ColorMapSize; k <= transparent_pixel; k++) + Red[k] = Green[k] = Blue[k] = 0xff; // white (color is irrelevant) + ColorMapSize = transparent_pixel + 1; + } + +#if (0) // TEST/DEBUG: fill color table to maximum size + for (int i = ColorMapSize; i < 256; i++) { + Red[i] = Green[i] = Blue[i] = 0; // black + } +#endif + + CHECK_ERROR + + // now read the LZW compressed image data + uchar *Image = new uchar[Width*Height]; int YC = 0, Pass = 0; /* Used to de-interlace the picture */ @@ -292,12 +355,13 @@ void Fl_GIF_Image::load_gif_(Fl_Image_Reader &rdr) int FreeCode = FirstFree; int OldCode = ClearCode; - // tables used by LZW decompresser: + // tables used by LZW decompressor: short int Prefix[4096]; uchar Suffix[4096]; int blocklen = rdr.read_byte(); uchar thisbyte = rdr.read_byte(); blocklen--; + CHECK_ERROR int frombit = 0; for (;;) { @@ -311,17 +375,21 @@ void Fl_GIF_Image::load_gif_(Fl_Image_Reader &rdr) if (frombit+CodeSize > 7) { if (blocklen <= 0) { blocklen = rdr.read_byte(); + CHECK_ERROR if (blocklen <= 0) break; } thisbyte = rdr.read_byte(); blocklen--; + CHECK_ERROR CurCode |= thisbyte<<8; } if (frombit+CodeSize > 15) { if (blocklen <= 0) { blocklen = rdr.read_byte(); + CHECK_ERROR if (blocklen <= 0) break; } thisbyte = rdr.read_byte(); blocklen--; + CHECK_ERROR CurCode |= thisbyte<<16; } CurCode = (CurCode>>frombit)&ReadMask; @@ -335,16 +403,34 @@ void Fl_GIF_Image::load_gif_(Fl_Image_Reader &rdr) continue; } - if (CurCode == EOFCode) break; + if (CurCode == EOFCode) + break; uchar OutCode[4097]; // temporary array for reversing codes uchar *tp = OutCode; 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!", rdr.name()); break;} + if (CurCode < FreeCode) { + i = CurCode; + } else if (CurCode == FreeCode) { + *tp++ = (uchar)FinChar; + i = OldCode; + } else { + Fl::error("Fl_GIF_Image: %s - LZW Barf at offset %ld", rdr.name(), rdr.tell()); + break; + } - while (i >= ColorMapSize) {*tp++ = Suffix[i]; i = Prefix[i];} + while (i >= ColorMapSize) { + if (i < FreeCode) { + *tp++ = Suffix[i]; + i = Prefix[i]; + } else { // FIXME - should never happen (?) + Fl::error("Fl_GIF_Image: %s - i(%d) >= FreeCode (%d) at offset %ld", + rdr.name(), i, FreeCode, rdr.tell()); + // NOTREACHED + i = FreeCode - 1; // fix broken index ??? + break; + } + } *tp++ = FinChar = i; do { *p++ = *--tp; @@ -363,21 +449,22 @@ void Fl_GIF_Image::load_gif_(Fl_Image_Reader &rdr) } while (tp > OutCode); if (OldCode != ClearCode) { - Prefix[FreeCode] = (short)OldCode; - Suffix[FreeCode] = FinChar; - FreeCode++; + if (FreeCode < 4096) { + 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: + // We are done reading the image, now convert to xpm: // allocate line pointer arrays: w(Width); @@ -452,4 +539,5 @@ void Fl_GIF_Image::load_gif_(Fl_Image_Reader &rdr) alloc_data = 1; delete[] Image; -} + +} // load_gif_() diff --git a/src/Fl_Image_Reader.h b/src/Fl_Image_Reader.h index 7d176acc1..2c62e4a27 100644 --- a/src/Fl_Image_Reader.h +++ b/src/Fl_Image_Reader.h @@ -82,6 +82,9 @@ public: // return the name or filename for this reader const char *name() { return pName; } + // skip a given number of bytes + void skip(unsigned int n) { seek(tell() + n); } + private: // open() sets this if we read from a file |
