mirror of https://github.com/fltk/fltk
- add error and EOF checks - fix transparent pixel index outside ColorMap (#271) - fix Fl_GIF_Image decoder bug (#274) - add Fl_Image_Reader::skip(unsigned int) - use new skip() method in GIF reader
This commit is contained in:
parent
4075a14715
commit
1d847fec00
|
@ -1,7 +1,7 @@
|
|||
//
|
||||
// GIF image header file for the Fast Light Tool Kit (FLTK).
|
||||
//
|
||||
// Copyright 1998-2020 by Bill Spitzak and others.
|
||||
// Copyright 1998-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
|
||||
|
@ -31,7 +31,7 @@ class FL_EXPORT Fl_GIF_Image : public Fl_Pixmap {
|
|||
public:
|
||||
|
||||
Fl_GIF_Image(const char* filename);
|
||||
Fl_GIF_Image(const char* imagename, const unsigned char *data);
|
||||
Fl_GIF_Image(const char* imagename, const unsigned char *data, const long length = -1);
|
||||
|
||||
protected:
|
||||
|
||||
|
|
|
@ -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,38 +105,66 @@ 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.
|
||||
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.
|
||||
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 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.
|
||||
|
||||
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.
|
||||
The destructor frees all memory and server resources that are used by
|
||||
the image.
|
||||
|
||||
\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.
|
||||
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.
|
||||
|
||||
\see Fl_GIF_Image::Fl_GIF_Image(const char *filename)
|
||||
\see Fl_Shared_Image
|
||||
If \p length is not used
|
||||
- it defaults to -1 (unlimited size)
|
||||
- buffer overruns will not be checked.
|
||||
|
||||
\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.
|
||||
|
||||
\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
|
||||
*/
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
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
|
||||
|
@ -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();
|
||||
CHECK_ERROR
|
||||
|
||||
if (ch==0xF9 && blocklen==4) { // Netscape animation extension
|
||||
|
||||
char bits;
|
||||
bits = rdr.read_byte();
|
||||
rdr.read_word(); // GETSHORT(delay);
|
||||
transparent_pixel = rdr.read_byte();
|
||||
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_()
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue