diff options
| author | wcout <wcout@users.noreply.github.com> | 2023-01-21 17:27:58 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-01-21 17:27:58 +0100 |
| commit | 2ddfd9d9492d9fc1df111ec9211dd1be4d424c35 (patch) | |
| tree | c766d0dfb3a2d7a75c275db2821d5bcf0e935a15 /src/Fl_GIF_Image.cxx | |
| parent | 1fc269b0d4c79b256cc57740d318f95dded8c340 (diff) | |
Animated GIF support (Fl_Anim_GIF_Image class) (#375)
Diffstat (limited to 'src/Fl_GIF_Image.cxx')
| -rw-r--r-- | src/Fl_GIF_Image.cxx | 626 |
1 files changed, 418 insertions, 208 deletions
diff --git a/src/Fl_GIF_Image.cxx b/src/Fl_GIF_Image.cxx index a8b6250da..93fe22f36 100644 --- a/src/Fl_GIF_Image.cxx +++ b/src/Fl_GIF_Image.cxx @@ -1,7 +1,7 @@ // // Fl_GIF_Image routines. // -// Copyright 1997-2021 by Bill Spitzak and others. +// Copyright 1997-2023 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,6 +74,16 @@ /* + Internally used structure to hold GIF color map data + during decoding. +*/ +struct ColorMap { + uchar Red[256]; + uchar Green[256]; + uchar Blue[256]; +}; + +/* This small helper function checks for read errors or end of file and does some cleanup if an error was found. It returns true (1) on error, false (0) otherwise. @@ -137,8 +147,8 @@ Fl_GIF_Image::Fl_GIF_Image(const char *filename) : 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. + \p imagename can be \c 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. @@ -205,193 +215,49 @@ Fl_GIF_Image::Fl_GIF_Image(const char *imagename, const unsigned char *data) : } } -/* - This method reads GIF image data and creates an RGB or RGBA image. The GIF - format supports only 1 bit for alpha. The final image data is stored in - a modified XPM format (Fl_GIF_Image is a subclass of Fl_Pixmap). - To avoid code duplication, we use an Fl_Image_Reader that reads data from - either a file or from memory. -*/ -void Fl_GIF_Image::load_gif_(Fl_Image_Reader &rdr) +Fl_GIF_Image::Fl_GIF_Image(const char *filename, bool anim) : + Fl_Pixmap((char *const*)0) { - char **new_data; // Data array - uchar *Image = 0L; // internal temporary image 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(); - 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 = rdr.read_word(); - int Height = rdr.read_word(); - - uchar ch = rdr.read_byte(); - CHECK_ERROR - char HasColormap = ((ch & 0x80) != 0); - int BitsPerPixel = (ch & 7) + 1; - int ColorMapSize; - if (HasColormap) { - ColorMapSize = 2 << (ch & 7); + Fl_Image_Reader rdr; + if (rdr.open(filename) == -1) { + Fl::error("Fl_GIF_Image: Unable to open %s!", filename); + ld(ERR_FILE_ACCESS); } else { - ColorMapSize = 0; - } - // int OriginalResolution = ((ch>>4)&7)+1; - // 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; - char has_transparent = 0; - uchar Red[256], Green[256], Blue[256]; /* color map */ - if (HasColormap) { - for (int i=0; i < ColorMapSize; i++) { - Red[i] = rdr.read_byte(); - Green[i] = rdr.read_byte(); - Blue[i] = rdr.read_byte(); - } - } - CHECK_ERROR - - int CodeSize; /* Code size, init from GIF header, increases... */ - char Interlace; - - // Main parser loop: parse "blocks" until an image is found or error - - for (;;) { - - int i = rdr.read_byte(); - CHECK_ERROR - int blocklen; - - if (i == 0x21) { // a "gif extension" - ch = rdr.read_byte(); // extension type - blocklen = 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; - } - 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 - // printf("Local Color Table at offset %ld\n", rdr.tell()); - 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(); - } - } - CHECK_ERROR - break; // okay, this is the image we want - } else if (i == 0x3b) { // Trailer (end of GIF data) - // printf("Trailer found at offset %ld\n", rdr.tell()); - Fl::error("%s: no image data found.", rdr.name()); - ld(ERR_NO_IMAGE); // this GIF file is "empty" (no image) - return; // terminate - } else { - Fl::error("%s: unknown GIF code 0x%02x at offset %ld", rdr.name(), i, rdr.tell()-1); - ld(ERR_FORMAT); // broken file - return; // terminate - } - CHECK_ERROR - - // skip all data (sub)blocks: - while (blocklen > 0) { - rdr.skip(blocklen); - blocklen = rdr.read_byte(); - } - // printf("End of data (sub)blocks at offset %ld\n", rdr.tell()); - } - - // 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; + load_gif_(rdr, anim); } +} - // Fix images w/o color table. The standard allows this and lets the - // decoder choose a default color table. The standard recommends the - // 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", rdr.name()); - BitsPerPixel = CodeSize - 1; - ColorMapSize = 1 << BitsPerPixel; - Red[0] = Green[0] = Blue[0] = 0; // black - Red[1] = Green[1] = Blue[1] = 255; // white - for (int i = 2; i < ColorMapSize; i++) { - Red[i] = Green[i] = Blue[i] = (uchar)(255 * i / (ColorMapSize - 1)); - } +Fl_GIF_Image::Fl_GIF_Image(const char *imagename, const unsigned char *data, const size_t length, bool anim) : + Fl_Pixmap((char *const*)0) +{ + Fl_Image_Reader rdr; + if (rdr.open(imagename, data, length) == -1) { + ld(ERR_FILE_ACCESS); + } else { + load_gif_(rdr, anim); } +} - // 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; - } +/** + The default constructor creates an empty GIF image. -#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 +*/ +Fl_GIF_Image::Fl_GIF_Image() : + Fl_Pixmap((char *const*)0) +{ +} - CHECK_ERROR - // now read the LZW compressed image data +/* + Internally used method to read from the LZW compressed data + stream 'rdr' and decode it to 'Image' buffer. - Image = new uchar[Width*Height]; + NOTE: This methode has been extracted from load_gif_() + in order to make the code more read/hand-able. +*/ +void Fl_GIF_Image::lzw_decode(Fl_Image_Reader &rdr, uchar *Image, + int Width, int Height, int CodeSize, int ColorMapSize, int Interlace) { int YC = 0, Pass = 0; /* Used to de-interlace the picture */ uchar *p = Image; uchar *eol = p+Width; @@ -419,10 +285,10 @@ void Fl_GIF_Image::load_gif_(Fl_Image_Reader &rdr) 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. */ + * 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) { @@ -455,8 +321,11 @@ void Fl_GIF_Image::load_gif_(Fl_Image_Reader &rdr) continue; } - if (CurCode == EOFCode) + if (CurCode == EOFCode) { + rdr.skip(blocklen); + blocklen = rdr.read_byte(); // Block-Terminator must follow! break; + } uchar OutCode[4097]; // temporary array for reversing codes uchar *tp = OutCode; @@ -515,47 +384,52 @@ void Fl_GIF_Image::load_gif_(Fl_Image_Reader &rdr) } OldCode = CurCode; } +} + - // We are done reading the image, now convert to xpm +/* + Internally used function to convert raw 'Image' data + to XPM format in an allocated buffer 'new_data'. - w(Width); - h(Height); - d(1); + NOTE: This function has been extracted from load_gif_() + in order to make the code more read/hand-able. +*/ +static char ** convert_to_xpm(uchar *Image, int Width, int Height, ColorMap &CMap, int ColorMapSize, int transparent_pixel) { // allocate line pointer arrays: - new_data = new char*[Height+2]; + char **new_data = new char*[Height+2]; // transparent pixel must be zero, swap if it isn't: - if (has_transparent && transparent_pixel != 0) { + if (transparent_pixel > 0) { // swap transparent pixel with zero - p = Image+Width*Height; + uchar *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 = CMap.Red[0]; + CMap.Red[0] = CMap.Red[transparent_pixel]; + CMap.Red[transparent_pixel] = t; - t = Green[0]; - Green[0] = Green[transparent_pixel]; - Green[transparent_pixel] = t; + t = CMap.Green[0]; + CMap.Green[0] = CMap.Green[transparent_pixel]; + CMap.Green[transparent_pixel] = t; - t = Blue[0]; - Blue[0] = Blue[transparent_pixel]; - Blue[transparent_pixel] = t; + t = CMap.Blue[0]; + CMap.Blue[0] = CMap.Blue[transparent_pixel]; + CMap.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; + uchar *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 base = transparent_pixel >= 0 && used[0] ? ' ' : ' '+1; int numcolors = 0; for (i = 0; i < ColorMapSize; i++) if (used[i]) { remap[i] = (uchar)(base++); @@ -563,6 +437,7 @@ void Fl_GIF_Image::load_gif_(Fl_Image_Reader &rdr) } // write the first line of xpm data (use suffix as temp array): + uchar Suffix[4096]; int length = snprintf((char*)(Suffix), sizeof(Suffix), "%d %d %d %d",Width,Height,-numcolors,1); new_data[0] = new char[length+1]; @@ -572,9 +447,9 @@ void Fl_GIF_Image::load_gif_(Fl_Image_Reader &rdr) 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]; + *p++ = CMap.Red[i]; + *p++ = CMap.Green[i]; + *p++ = CMap.Blue[i]; } // remap the image data: @@ -588,9 +463,344 @@ void Fl_GIF_Image::load_gif_(Fl_Image_Reader &rdr) new_data[i + 2][Width] = 0; } - data((const char **)new_data, Height + 2); - alloc_data = 1; + return new_data; +} + + +/* + This method reads GIF image data and creates an RGB or RGBA image. The GIF + format supports only 1 bit for alpha. The final image data is stored in + a modified XPM format (Fl_GIF_Image is a subclass of Fl_Pixmap). + To avoid code duplication, we use an Fl_Image_Reader that reads data from + either a file or from memory. + + wcout 2022/01/20 - Added animated GIF support: + If the load_gif_() method is called with 'anim=true', then not only the first + image is decoded (as with Fl_GIF_Image), but all contained images are read. + The new Fl_Anim_GIF_Image class is derived from Fl_GIF_Image and utilises this + feature in order to avoid code duplication of the GIF decoding routines. + The first image is in this case (additionally) stored in the usual way as described + above (making the Fl_Anim_GIF_Image a normal Fl_GIF_Image too). + All subsequent images are only decoded (and not converted to XPM) and passed + to Fl_Anim_GIF_Image, which stores them on its own (in RGBA format). +*/ +void Fl_GIF_Image::load_gif_(Fl_Image_Reader &rdr, bool anim/*=false*/) +{ + uchar *Image = 0L; // internal temporary image data array + int frame = 0; + + // Needed for proper decoding of *all* images in file (Fl_Anim_GIF_Image) + uchar background_color_index = 0; + GIF_FRAME::CPAL GlobalColorTable[256]; + bool HasGlobalColorTable = false; + GIF_FRAME::CPAL LocalColorTable[256]; + bool HasLocalColorTable = false; + + 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(); + 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 ScreenWidth = rdr.read_word(); + int ScreenHeight = rdr.read_word(); + int Width = ScreenWidth; + int Height = ScreenHeight; + + uchar ch = rdr.read_byte(); + CHECK_ERROR + char HasColormap = ((ch & 0x80) != 0); + HasGlobalColorTable = HasColormap; + memset(GlobalColorTable, 0, sizeof(GlobalColorTable)); + int BitsPerPixel = (ch & 7) + 1; + int ColorMapSize = HasColormap ? 2 << (ch & 7) : 0; + // int OriginalResolution = ((ch>>4)&7)+1; + // int SortedTable = (ch&8)!=0; + background_color_index = 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; + char has_transparent = 0; + char user_input = 0; + int delay = 0; + int dispose = 0; + int XPos = 0; + int YPos = 0; + struct ColorMap CMap; /* color map */ + if (HasColormap) { + for (int i=0; i < ColorMapSize; i++) { + CMap.Red[i] = rdr.read_byte(); + CMap.Green[i] = rdr.read_byte(); + CMap.Blue[i] = rdr.read_byte(); + // store away for reading of further images in file (Fl_Anim_GIF_Image) + // because the values are changed during processing. + GlobalColorTable[i].r = CMap.Red[i]; + GlobalColorTable[i].g = CMap.Green[i]; + GlobalColorTable[i].b = CMap.Blue[i]; + } + } + CHECK_ERROR + + char Interlace = 0; + + // Main parser loop: parse "blocks" until an image is found or error + + for (;;) { + + int i = rdr.read_byte(); + CHECK_ERROR + int blocklen = 0; + + if (i == 0x21) { // a "gif extension" + ch = rdr.read_byte(); // extension type + blocklen = rdr.read_byte(); + CHECK_ERROR + + if (ch == 0xF9 && blocklen == 4) { // Graphic Control Extension + // printf("Graphic Control Extension at offset %ld\n", rdr.tell()-2); + uchar bits = rdr.read_byte(); // Packed Fields xxxDDDUT + dispose = (bits >> 2) & 7; + delay = rdr.read_word(); // Delay Time + transparent_pixel = rdr.read_byte(); // Transparent Color Index + blocklen = rdr.read_byte(); // Block Terminator (must be zero) + CHECK_ERROR + has_transparent = (bits & 1) ? 1 : 0; + user_input = (bits & 2) ? 1 : 0; + } + else if (ch == 0xFF) { // Application Extension + // printf("Application Extension at offset %ld, length = %d\n", rdr.tell()-3, blocklen); + uchar buf[512]; + memset(buf, 0, sizeof(buf)); + for (i=0; i<blocklen; i++) buf[i] = rdr.read_byte(); + blocklen = rdr.read_byte(); // read next Data Sub-block too for NETSCAPE ext. + CHECK_ERROR + if (blocklen) { + while (blocklen--) buf[i++] = rdr.read_byte(); + blocklen = rdr.read_byte(); + } + CHECK_ERROR + + // Notify derived class on loaded extension data + + GIF_FRAME f(frame, buf); + on_extension_data(f); + } + 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()); + XPos = rdr.read_word(); // Image Left Position + YPos = 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); + HasLocalColorTable = ch & 0x80; + 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); + memset(LocalColorTable, 0, sizeof(LocalColorTable)); + for (i=0; i < ColorMapSize; i++) { + CMap.Red[i] = rdr.read_byte(); + CMap.Green[i] = rdr.read_byte(); + CMap.Blue[i] = rdr.read_byte(); + // store away for reading of further images in file (Fl_Anim_GIF_Image) + // because the values are changed during processing. + LocalColorTable[i].r = CMap.Red[i]; + LocalColorTable[i].g = CMap.Green[i]; + LocalColorTable[i].b = CMap.Blue[i]; + } + } + CHECK_ERROR + + // read image data + + // printf("Image Data at offset %ld\n", rdr.tell()); + + int CodeSize = rdr.read_byte(); // LZW initial Code Size (increases...) + CHECK_ERROR + if (CodeSize < 2 || CodeSize > 8) { // though invalid, other decoders accept an use it + Fl::warning("Fl_GIF_Image: %s invalid LZW-initial code size %d.\n", rdr.name(), CodeSize); + } + CodeSize++; + + // Fix images w/o color table. The standard allows this and lets the + // decoder choose a default color table. The standard recommends the + // 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", rdr.name()); + BitsPerPixel = CodeSize - 1; + ColorMapSize = 1 << BitsPerPixel; + CMap.Red[0] = CMap.Green[0] = CMap.Blue[0] = 0; // black + CMap.Red[1] = CMap.Green[1] = CMap.Blue[1] = 255; // white + for (int i = 2; i < ColorMapSize; i++) { + CMap.Red[i] = CMap.Green[i] = CMap.Blue[i] = (uchar)(255 * i / (ColorMapSize - 1)); + } + } + + // Workaround for broken GIF files... + BitsPerPixel = CodeSize - 1; + if (1 << BitsPerPixel <= 256) + ColorMapSize = 1 << BitsPerPixel; + + // Fix transparent pixel index outside ColorMap (Issue #271) + if (has_transparent && transparent_pixel >= ColorMapSize) { + for (int k = ColorMapSize; k <= transparent_pixel; k++) + CMap.Red[k] = CMap.Green[k] = CMap.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++) { + CMap.Red[i] = CMap.Green[i] = CMap.Blue[i] = 0; // black + } +#endif + + CHECK_ERROR + + // now read the LZW compressed image data + + Image = new uchar[Width*Height]; + lzw_decode(rdr, Image, Width, Height, CodeSize, ColorMapSize, Interlace); + if (ld()) return; // CHECK_ERROR aborted already + + // Notify derived class on loaded image data + + GIF_FRAME gf(frame, ScreenWidth, ScreenHeight, XPos, YPos, Width, Height, Image); + gf.disposal(dispose, user_input ? -delay - 1 : delay); + gf.colors(ColorMapSize, background_color_index, has_transparent ? transparent_pixel : -1); + GIF_FRAME::CPAL cpal[256] = { { 0 } }; + if (HasLocalColorTable) + gf.cpal = LocalColorTable; + else if (HasGlobalColorTable) + gf.cpal = GlobalColorTable; + else { + for (i=0; i < ColorMapSize; i++) { + cpal[i].r = CMap.Red[i]; cpal[i].g = CMap.Green[i]; cpal[i].b = CMap.Blue[i]; + } + gf.cpal = cpal; + } +#if (0) // TEST/DEBUG: output palette values + printf("palette:\n"); + for (i=0; i<ColorMapSize; i++) { + printf("%d: #%02X%02X%02X\n", i, gf.cpal[i].r, gf.cpal[i].g, gf.cpal[i].b); + } +#endif + on_frame_data(gf); + + // We are done reading the image, now convert to xpm (first image only) + if (!frame) { + if (anim && ( (Width != ScreenWidth) || (Height != ScreenHeight) )) { + // if we are reading this for Fl_Anim_GIF_Image, we must apply offsets + w(ScreenWidth); + h(ScreenHeight); + d(1); + uchar *moved_image = new uchar[ScreenWidth*ScreenHeight]; + memset(moved_image, has_transparent ? transparent_pixel : 0, ScreenWidth*ScreenHeight); + int xstart = XPos; if (xstart < 0) xstart = 0; + int ystart = YPos; if (ystart < 0) ystart = 0; + int xmax = XPos + Width; if (xmax > ScreenWidth) xmax = ScreenWidth; + int ymax = YPos + Height; if (ymax > ScreenHeight) ymax = ScreenHeight; + for (int y = ystart; y<ymax; y++) { + uchar *src = Image + (y-YPos) * Width + (xstart-XPos); + uchar *dst = moved_image + y*ScreenWidth + xstart; + memcpy(dst, src, xmax-xstart); + } + char **new_data = convert_to_xpm(moved_image, ScreenWidth, ScreenHeight, CMap, ColorMapSize, has_transparent ? transparent_pixel : -1); + data((const char **)new_data, Height + 2); + alloc_data = 1; + delete[] moved_image; + } else { + // Fl_GIF_Image does not apply offsets and just show the first frame at 0, 0 + w(Width); + h(Height); + d(1); + char **new_data = convert_to_xpm(Image, Width, Height, CMap, ColorMapSize, has_transparent ? transparent_pixel : -1); + data((const char **)new_data, Height + 2); + alloc_data = 1; + } + } + + delete[] Image; + Image = NULL; + + if (!anim) + break; // okay, it is only the first image we want + frame++; // continue to read more images (of animated GIF) + // end of Image Descriptor block processing + } else if (i == 0x3b) { // Trailer (end of GIF data) + // printf("Trailer found at offset %ld\n", rdr.tell()); + if (!frame) { + Fl::error("%s: no image data found.", rdr.name()); + ld(ERR_NO_IMAGE); // this GIF file is "empty" (no image) + } + return; // terminate + } else { + Fl::error("%s: unknown GIF code 0x%02x at offset %ld", rdr.name(), i, rdr.tell()-1); + ld(ERR_FORMAT); // broken file + return; // terminate + } + CHECK_ERROR + + // skip all data (sub)blocks: + while (blocklen > 0) { + rdr.skip(blocklen); + blocklen = rdr.read_byte(); + } + // printf("End of data (sub)blocks at offset %ld\n", rdr.tell()); + } - delete[] Image; } // load_gif_() + + +/** + The protected load() methods are used by Fl_Anim_GIF_Image + to request loading of animated GIF's. + +*/ +void Fl_GIF_Image::load(const char* filename, bool anim) +{ + Fl_Image_Reader rdr; + if (rdr.open(filename) == -1) { + Fl::error("Fl_GIF_Image: Unable to open %s!", filename); + ld(ERR_FILE_ACCESS); + } else { + load_gif_(rdr, anim); + } +} + +void Fl_GIF_Image::load(const char *imagename, const unsigned char *data, const size_t len, bool anim) +{ + Fl_Image_Reader rdr; + if (rdr.open(imagename, data, len) == -1) { + ld(ERR_FILE_ACCESS); + } else { + load_gif_(rdr, anim); + } +} |
