FLTK logo

[master] 2ddfd9d - Animated GIF support (Fl_Anim_GIF_Image class) (#375)

FLTK matrix user chat room
(using Element browser app)   FLTK gitter user chat room   GitHub FLTK Project   FLTK News RSS Feed  
  FLTK Apps      FLTK Library      Forums      Links     Login 
 All Forums  |  Back to fltk.commit  ]
 
Previous Message ]Next Message ]

[master] 2ddfd9d - Animated GIF support (Fl_Anim_GIF_Image class) (#375) "wcout" Jan 21, 2023  
 
commit 2ddfd9d9492d9fc1df111ec9211dd1be4d424c35
Author:     wcout <wcout@users.noreply.github.com>
AuthorDate: Sat Jan 21 17:27:58 2023 +0100
Commit:     GitHub <noreply@github.com>
CommitDate: Sat Jan 21 17:27:58 2023 +0100

    Animated GIF support (Fl_Anim_GIF_Image class) (#375)

 FL/Fl_Anim_GIF_Image.H            |  185 ++++++
 FL/Fl_GIF_Image.H                 |   45 +-
 FL/Fl_Image.H                     |    3 +
 examples/CMakeLists.txt           |    4 +
 examples/Makefile                 |    6 +-
 examples/animgifimage-play.cxx    |  237 +++++++
 examples/animgifimage-resize.cxx  |  185 ++++++
 examples/animgifimage-simple.cxx  |   40 ++
 examples/animgifimage.cxx         |  303 +++++++++
 fluid/Fluid_Image.cxx             |   46 +-
 fluid/Fluid_Image.h               |    1 +
 fluid/about_panel.cxx             |  351 ++++------
 fluid/about_panel.fl              |   11 +-
 fluid/icons/fluid.animated.gif    |  Bin 0 -> 2545 bytes
 src/CMakeLists.txt                |    1 +
 src/Fl_Anim_GIF_Image.cxx         | 1279 +++++++++++++++++++++++++++++++++++++
 src/Fl_GIF_Image.cxx              |  626 ++++++++++++------
 src/Makefile                      |    1 +
 src/fl_images_core.cxx            |    4 +-
 src/makedepend                    |    5 +
 test/CMakeLists.txt               |    2 +-
 test/Makefile                     |    5 +-
 test/help_dialog.cxx              |   11 +
 test/help_dialog.html             |    3 +
 test/pixmap.cxx                   |   54 +-
 test/pixmap_browser.cxx           |   19 +
 test/pixmaps/animated_fluid_gif.h |  118 ++++
 test/pixmaps/fltk_animated.gif    |  Bin 0 -> 116547 bytes
 test/pixmaps/fltk_animated2.gif   |  Bin 0 -> 12618 bytes
 test/pixmaps/fltk_animated3.gif   |  Bin 0 -> 60554 bytes
 test/tiled_image.cxx              |    5 +-
 test/utf8.cxx                     |    4 +-
 32 files changed, 3082 insertions(+), 472 deletions(-)

diff --git FL/Fl_Anim_GIF_Image.H FL/Fl_Anim_GIF_Image.H
new file mode 100644
index 0000000..bb33f3f
--- /dev/null
+++ FL/Fl_Anim_GIF_Image.H
@@ -0,0 +1,185 @@
+//
+// Fl_Anim_GIF_Image class header for the Fast Light Tool Kit (FLTK).
+//
+// Copyright 2016-2022 by Christian Grabner <wcout@gmx.net>.
+//
+// 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:
+//
+//     https://www.fltk.org/COPYING.php
+//
+// Please see the following page on how to report bugs and issues:
+//
+//     https://www.fltk.org/bugs.php
+//
+
+#ifndef Fl_Anim_Gif_Image_H
+#define Fl_Anim_Gif_Image_H
+
+// forward declarations
+class Fl_Image;
+class Fl_Widget;
+
+#include <FL/Fl_GIF_Image.H>
+
+// Load and display animater GIF images
+class FL_EXPORT Fl_Anim_GIF_Image : public Fl_GIF_Image {
+
+  class FrameInfo; // internal helper class
+
+public:
+
+  /**
+   When opening an Fl_Anim_GIF_Image there are some options
+   that can be passed in a `flags` value.
+   */
+  enum Flags {
+    /**
+     This flag indicates to the loader that it should not start
+     the animation immediately after successful load, which is
+     the default.
+     It can be started later using the \ref start() method.
+     */
+    DONT_START = 1,
+    /**
+     This flag indicates to the loader that it should not
+     resize the canvas widget of the animation to the dimensions
+     of the animation, which is the default.
+     Needed for special use cases.
+     */
+    DONT_RESIZE_CANVAS = 2,
+    /**
+     This flag indicates to the loader that it should not
+     set the animation as \ref image() member of the canvas widget,
+     which is the default.
+     Needed for special use cases.
+     */
+    DONT_SET_AS_IMAGE = 4,
+    /**
+     Often frames change just a small area of the animation canvas.
+     This flag indicates to the loader to try using less memory
+     by storing frame data not as canvas-sized images but use the
+     sizes defined in the GIF file.
+     The drawbacks are higher cpu usage during playback and maybe
+     minor artefacts when resized.
+     */
+    OPTIMIZE_MEMORY = 8,
+    /**
+     This flag can be used to print informations about the
+     decoding process to the console.
+     */
+    LOG_FLAG = 64,
+    /**
+     This flag can be used to print even more informations about
+     the decoding process to the console.
+     */
+    DEBUG_FLAG = 128
+  };
+
+  // -- constructors and destructor
+  Fl_Anim_GIF_Image(const char *filename, Fl_Widget *canvas = 0, unsigned short flags = 0);
+  Fl_Anim_GIF_Image(const char* imagename, const unsigned char *data,
+                    const size_t length, Fl_Widget *canvas = 0,
+                    unsigned short flags = 0);
+  Fl_Anim_GIF_Image();
+  ~Fl_Anim_GIF_Image() FL_OVERRIDE;
+
+  // -- file handling
+  bool load(const char *name, const unsigned char *imgdata=NULL, size_t imglength=0);
+  bool valid() const;
+
+  // -- getters and setters
+  void frame_uncache(bool uncache);
+  bool frame_uncache() const;
+  double delay(int frame_) const;
+  void delay(int frame, double delay);
+  void canvas(Fl_Widget *canvas, unsigned short flags = 0);
+  Fl_Widget *canvas() const;
+  int canvas_w() const;
+  int canvas_h() const;
+  bool is_animated() const;
+  const char *name() const;
+  void speed(double speed);
+  double speed() const;
+
+  // -- animation
+  int frames() const;
+  void frame(int frame);
+  int frame() const;
+  Fl_Image *image() const;
+  Fl_Image *image(int frame) const;
+  bool start();
+  bool stop();
+  bool next();
+
+  /** Return if the animation is currently running or stopped.
+   \return true if the animation is running
+   */
+  bool playing() const { return valid() && Fl::has_timeout(cb_animate, (void *)this); }
+
+  // -- image data
+  Fl_Anim_GIF_Image& resize(int w, int h);
+  Fl_Anim_GIF_Image& resize(double scale);
+  int frame_x(int frame) const;
+  int frame_y(int frame) const;
+  int frame_w(int frame) const;
+  int frame_h(int frame) const;
+
+  // -- overriden methods
+  void color_average(Fl_Color c, float i) FL_OVERRIDE;
+  Fl_Image *copy(int W, int H) const FL_OVERRIDE;
+  Fl_Image *copy() const { return Fl_Pixmap::copy(); }
+  void desaturate() FL_OVERRIDE;
+  void draw(int x, int y, int w, int h, int cx = 0, int cy = 0) FL_OVERRIDE;
+  void uncache() FL_OVERRIDE;
+
+  // -- debugging and logging
+  int debug() const;
+
+  // -- static methods
+  static int frame_count(const char *name, const unsigned char *imgdata = NULL, size_t imglength = 0);
+
+  /**
+   The loop flag can be used to (dis-)allow loop count.
+   If set (which is the default), the animation will be
+   stopped after the number of repeats specified in the
+   GIF file (typically this count is set to 'forever' anyway).
+   If cleared the animation will always be 'forever',
+   regardless of what is specified in the GIF file.
+   */
+  static bool loop;
+
+  /**
+   The min_delay value can be used to set a minimum value
+   for the frame delay for playback. This is to prevent
+   CPU hogs caused by images with very low delay rates.
+   This is a global value for all Fl_Anim_GIF_Image objects.
+   */
+  static double min_delay;
+
+protected:
+
+  bool next_frame();
+  void clear_frames();
+  void set_frame(int frame);
+
+  static void cb_animate(void *d);
+  void scale_frame();
+  void set_frame();
+  void on_frame_data(Fl_GIF_Image::GIF_FRAME &f) FL_OVERRIDE;
+  void on_extension_data(Fl_GIF_Image::GIF_FRAME &f) FL_OVERRIDE;
+
+private:
+
+  char *name_;
+  unsigned short flags_;
+  Fl_Widget *canvas_;
+  bool uncache_;
+  bool valid_;
+  int frame_; // current frame
+  double speed_;
+  FrameInfo *fi_;
+};
+
+#endif // Fl_Anim_Gif_Image_H
diff --git FL/Fl_GIF_Image.H FL/Fl_GIF_Image.H
index 4871bf5..248b61c 100644
--- FL/Fl_GIF_Image.H
+++ FL/Fl_GIF_Image.H
@@ -1,7 +1,7 @@
 //
 // GIF image header file for the Fast Light Tool Kit (FLTK).
 //
-// Copyright 1998-2021 by Bill Spitzak and others.
+// Copyright 1998-2022 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
@@ -36,10 +36,51 @@ public:
   // constructor with length (since 1.4.0)
   Fl_GIF_Image(const char* imagename, const unsigned char *data, const size_t length);
 
+  static bool is_animated(const char *name_);
+  /** Sets how the shared image core routine should treat animated GIF files.
+   The default is to treat them as ordinary GIF's e.g. it creates a Fl_GIF_Image object.
+   If this variable is set, then an animated GIF object Fl_Anim_GIF_Image is created.
+   */
+  static bool animate;
+
 protected:
 
-  void load_gif_(class Fl_Image_Reader &rdr);
+  // Proteced constructors needed for animated GIF support through Fl_Anim_GIF_Image.
+  Fl_GIF_Image(const char* filename, bool anim);
+  Fl_GIF_Image(const char* imagename, const unsigned char *data, const size_t length, bool anim);
+  // Protected default constructor needed for Fl_Anim_GIF_Image.
+  Fl_GIF_Image();
+
+  void load_gif_(class Fl_Image_Reader &rdr, bool anim=false);
+
+  void load(const char* filename, bool anim);
+  void load(const char* imagename, const unsigned char *data, const size_t length, bool anim);
+
+  // Internal structure to "glue" animated GIF support into Fl_GIF_Image.
+  // This data is passed during decoding to the Fl_Anim_GIF_Image class.
+  struct GIF_FRAME {
+    int ifrm, width, height, x, y, w, h,
+        clrs, bkgd, trans,
+        dispose, delay;
+    const uchar *bptr;
+    const struct CPAL {
+      uchar r, g, b;
+    } *cpal;
+    GIF_FRAME(int frame, uchar *data) : ifrm(frame), bptr(data) {}
+    GIF_FRAME(int frame, int W, int H, int fx, int fy, int fw, int fh, uchar *data) :
+      ifrm(frame), width(W), height(H), x(fx), y(fy), w(fw), h(fh), bptr(data) {}
+    void disposal(int mode, int delay) { dispose = mode; this->delay = delay; }
+    void colors(int nclrs, int bg, int tp) { clrs = nclrs; bkgd = bg; trans = tp; }
+  };
+
+  // Internal virtual methods, which are called during decoding to pass data
+  // to the Fl_Anim_GIF_Image class.
+  virtual void on_frame_data(GIF_FRAME &gf) {}
+  virtual void on_extension_data(GIF_FRAME &gf) {}
+
+private:
 
+  void lzw_decode(Fl_Image_Reader &rdr, uchar *Image, int Width, int Height, int CodeSize, int ColorMapSize, int Interlace);
 };
 
 #endif
diff --git FL/Fl_Image.H FL/Fl_Image.H
index fb1ddf7..b296704 100644
--- FL/Fl_Image.H
+++ FL/Fl_Image.H
@@ -272,6 +272,9 @@ public:
 
    An internal copy is made of the original image before
    changes are applied, to avoid modifying the original image.
+
+   \note The RGB color of \ref FL_BACKGROUND_COLOR may change when the
+   connection to the display is made. See fl_open_display().
    */
   void inactive() { color_average(FL_GRAY, .33f); }
   virtual void desaturate();
diff --git examples/CMakeLists.txt examples/CMakeLists.txt
index 2bc3355..faa6d7d 100644
--- examples/CMakeLists.txt
+++ examples/CMakeLists.txt
@@ -81,6 +81,10 @@ set (FLUID_SOURCES
 ############################################################
 
 set (IMAGE_SOURCES
+  animgifimage
+  animgifimage-play
+  animgifimage-resize
+  animgifimage-simple
   howto-simple-svg
 )
 
diff --git examples/Makefile examples/Makefile
index dc312aa..946eda8 100644
--- examples/Makefile
+++ examples/Makefile
@@ -22,7 +22,11 @@ SHELL = /bin/sh
 .SILENT:
 
 # Executables
-ALL = browser-simple$(EXEEXT) \
+ALL = animgifimage$(EXEEXT) \
+      animgifimage-play$(EXEEXT) \
+      animgifimage-simple$(EXEEXT) \
+      animgifimage-resize$(EXEEXT) \
+      browser-simple$(EXEEXT) \
       cairo-draw-x$(EXEEXT) \
       chart-simple$(EXEEXT) \
       draggable-group$(EXEEXT) \
diff --git examples/animgifimage-play.cxx examples/animgifimage-play.cxx
new file mode 100644
index 0000000..4a319f8
--- /dev/null
+++ examples/animgifimage-play.cxx
@@ -0,0 +1,237 @@
+//
+//  Demonstrates how to play an animated GIF file
+//  under application control frame by frame if
+//  this is needed.
+//  Also demonstrates how to use a single animation
+//  object to load multiple animations.
+//
+//  animgifimage <file> [-r] [-s speed_factor]
+//
+//  Multiple files can be specified e.g. testsuite/*
+//
+//  Use keys '+'/'-'/Enter to change speed, ' ' to pause.
+//  Right key changes to next frame in paused mode.
+//  'n' changes to next file, 'r' toggles reverse play.
+//
+#include <FL/Fl_Anim_GIF_Image.H>
+#include <FL/Fl_Box.H>
+#include <FL/Fl_Double_Window.H>
+#include <FL/Fl.H>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+static double speed_factor = 1.; // slow down/speed up playback by factor
+static bool reverse = false;  // true = play animation backwards
+static bool paused = false;  // flag for paused animation
+static bool frame_info = false; // flag to update current frame info in title
+static Fl_Anim_GIF_Image animgif; // the animation object
+static char **Argv = 0; // copy of main() argv[]
+static int Argc = 0;    // copy of main() argc
+static int current_arg = 0; // current index in argv[]
+
+static int next_arg() {
+  while (1) {
+    current_arg++;
+    if (current_arg >= Argc) {
+      current_arg = 1;
+    }
+    if (Argv[current_arg]) break;
+  }
+  return current_arg;
+}
+
+static const char *next_file() {
+  while (Argv[next_arg()][0] == '-') ;
+  return Argv[current_arg];
+}
+
+static void set_title() {
+  char buf[200];
+  char fi[50];
+  if (frame_info)
+    snprintf(fi, sizeof(fi), "frame %d/%d", animgif.frame() + 1, animgif.frames());
+  else
+    snprintf(fi, sizeof(fi), "%d frames", animgif.frames());
+  snprintf(buf, sizeof(buf), "%s (%s) x %3.2f %s%s",
+    Argv[current_arg], fi,
+    speed_factor, reverse ? "reverse" : "",
+    paused ? " PAUSED" : "");
+
+  Fl::first_window()->copy_label(buf);
+}
+
+static void cb_anim(void *d_) {
+  Fl_Anim_GIF_Image *animgif = (Fl_Anim_GIF_Image *)d_;
+  int frame(animgif->frame());
+
+  // switch to next/previous frame
+  if (reverse) {
+    animgif->canvas()->window()->redraw();
+    frame--;
+    if (frame < 0) {
+      frame = animgif->frames() - 1;
+    }
+  }
+  else {
+    frame++;
+    if (frame >= animgif->frames()) {
+      frame = 0;
+    }
+  }
+  // set the frame (and update canvas)
+  animgif->frame(frame);
+
+  // setup timer for next frame
+  if (!paused && animgif->delay(frame)) {
+    Fl::repeat_timeout(animgif->delay(frame) / speed_factor, cb_anim, d_);
+  }
+  if (frame_info)
+    set_title();
+}
+
+static void next_frame() {
+  cb_anim(&animgif);
+}
+
+static void toggle_pause() {
+  paused = !paused;
+  set_title();
+  if (paused)
+    Fl::remove_timeout(cb_anim, &animgif);
+  else
+    next_frame();
+  set_title();
+}
+
+static void toggle_info() {
+  frame_info = !frame_info;
+  set_title();
+}
+
+static void toggle_reverse() {
+  reverse = !reverse;
+  set_title();
+}
+
+static void zoom(bool out) {
+  int W = animgif.w();
+  int H = animgif.h();
+  // Note: deliberately no range check (use key 'N' to reset)
+  static const double f = 1.05;
+  if (out)
+    animgif.resize((double)W/f, (double)H/f);
+  else
+    animgif.resize(f*W, f*H);
+}
+
+static void change_speed(int dir_) {
+  if (dir_> 0) {
+    speed_factor += (speed_factor < 1) ? 0.01 : 0.1;
+    if (speed_factor > 100)
+      speed_factor = 100.;
+  }
+  else if (dir_ < 0) {
+    speed_factor -= (speed_factor > 1) ? 0.1 : 0.01;
+    if (speed_factor < 0.01)
+      speed_factor = 0.01;
+  }
+  else {
+    speed_factor = 1.;
+  }
+  set_title();
+}
+
+static void load_next() {
+  Fl::remove_timeout(cb_anim, &animgif);
+  paused = false;
+  animgif.load(next_file());
+  animgif.canvas()->window()->redraw();
+  // check if loading succeeded
+  printf("valid: %d frames: %d\n", animgif.valid(), animgif.frames());
+  if (animgif.valid()) {
+    printf("play '%s'%s with %3.2f x speed\n", animgif.name(),
+       (reverse ? " reverse" : ""), speed_factor);
+    animgif.frame(reverse ? animgif.frames() - 1 : 0);
+    // setup first timeout, but check for zero-delay (normal GIF)!
+    if (animgif.delay(animgif.frame())) {
+      Fl::add_timeout(animgif.delay(animgif.frame()) / speed_factor, cb_anim, &animgif);
+    }
+  }
+  set_title();
+}
+
+static int events(int event_) {
+  if (event_ == FL_SHORTCUT && Fl::first_window()) {
+    switch (Fl::event_key()) {
+      case '+': change_speed(1); break;
+      case '-': change_speed(-1); break;
+      case FL_Enter: change_speed(0); break;
+      case 'n': load_next(); break;
+      case 'z': zoom(Fl::event_shift()); break;
+      case 'i': toggle_info(); break; // Note: this can raise cpu usage considerably!
+      case 'r': toggle_reverse(); break;
+      case ' ': toggle_pause(); break;
+      case FL_Right:
+        if (paused && Fl::get_key(FL_Right)) next_frame();
+        break;
+      default:
+        return 0;
+    }
+    Fl::first_window()->redraw();
+    return 1;
+  }
+  return 0;
+}
+
+int main(int argc, char *argv[]) {
+  // setup play parameters from args
+  Argv = argv;
+  Argc = argc;
+  int n = 0;
+  for (int i = 1; i < argc; i++) {
+    if (!strcmp(argv[i], "-r"))
+      reverse = !reverse;
+    else if (!strcmp(argv[i], "-s") && i + 1 < argc) {
+      i++;
+      speed_factor = atof(argv[i]);
+      argv[i] = 0;
+    }
+    else if (argv[i][0] != '-') {
+      n++;
+      continue;
+    }
+    else {
+      printf("Invalid argument: '%s'\n", argv[i]);
+      exit(1);
+    }
+  }
+  if (!n) {
+     fprintf(stderr, "Test program for application controlled GIF animation.\n");
+     fprintf(stderr, "Please specify one or more image files!\n");
+     exit(0);
+  }
+  if (speed_factor < 0.01 || speed_factor > 100)
+    speed_factor = 1.;
+
+  Fl_Double_Window win(800, 600);
+
+  // prepare a canvas for the animation
+  // (we want to show it in the center of the window)
+  Fl_Box canvas(0, 0, win.w(), win.h());
+  Fl_Box help(0, win.h()-20, win.w(), 20, "Keys: N=next file, I=toggle info, R=play reverse, +/-/Enter/Space=change speed, Z=Zoom");
+  win.resizable(win);
+
+  win.end();
+  win.show();
+  Fl::add_handler(events);
+
+  // use the 'DONT_RESIZE_CANVAS' flag to tell the animation
+  // not to change the canvas size (which is the default).
+  unsigned short flags = Fl_Anim_GIF_Image::DONT_RESIZE_CANVAS;
+//  flags |= Fl_Anim_GIF_Image::DEBUG_FLAG|Fl_Anim_GIF_Image::LOG_FLAG;
+  animgif.canvas(&canvas, flags);
+
+  load_next();
+  return Fl::run();
+}
diff --git examples/animgifimage-resize.cxx examples/animgifimage-resize.cxx
new file mode 100644
index 0000000..1417255
--- /dev/null
+++ examples/animgifimage-resize.cxx
@@ -0,0 +1,185 @@
+//
+//  Test program for Fl_Anim_GIF_Image::copy().
+//
+#include <FL/Fl_Anim_GIF_Image.H>
+#include <FL/Fl_Image.H>
+#include <FL/Fl_Box.H>
+#include <FL/Fl_Double_Window.H>
+#include <FL/Fl.H>
+#include <FL/fl_draw.H>
+#include <stdio.h>
+#include <stdlib.h>
+
+static Fl_Anim_GIF_Image *orig = 0;
+static bool draw_grid = true;
+
+static int events(int event_) {
+  if (event_ == FL_SHORTCUT && Fl::first_window()) {
+    if (Fl::event_key()=='g') {
+      draw_grid = !draw_grid;
+      printf("grid: %s\n", (draw_grid ? "ON" : "OFF"));
+    }
+    else if (Fl::event_key()=='b') {
+      if (Fl_Image::scaling_algorithm() != FL_RGB_SCALING_BILINEAR)
+        Fl_Image::scaling_algorithm(FL_RGB_SCALING_BILINEAR);
+      else
+        Fl_Image::scaling_algorithm(FL_RGB_SCALING_NEAREST);
+      printf("bilenear: %s\n", (Fl_Image::scaling_algorithm() != FL_RGB_SCALING_BILINEAR ? "OFF" : "ON"));
+    }
+    else
+      return 0;
+    Fl::first_window()->redraw();
+  }
+  return 1;
+}
+
+class Canvas : public Fl_Box {
+  typedef Fl_Box Inherited;
+public:
+  Canvas(int x, int y, int w, int h) :
+    Inherited(x, y, w, h) {}
+  void draw() FL_OVERRIDE {
+    if (draw_grid) {
+      // draw a transparency grid as background
+      static const Fl_Color C1 = fl_rgb_color(0xcc, 0xcc, 0xcc);
+      static const Fl_Color C2 = fl_rgb_color(0x88, 0x88, 0x88);
+      static const int SZ = 8;
+      for (int y = 0; y < h(); y += SZ) {
+        for (int x = 0; x < w(); x += SZ) {
+          fl_color(x%(SZ * 2) ? y%(SZ * 2) ? C1 : C2 : y%(SZ * 2) ? C2 : C1);
+          fl_rectf(x, y, 32, 32);
+        }
+      }
+    }
+    // draw the current image frame over the grid
+    Inherited::draw();
+  }
+  void do_resize(int W, int H) {
+    if (image() && (image()->w() != W || image()->h() != H)) {
+      Fl_Anim_GIF_Image *animgif = (Fl_Anim_GIF_Image *)image();
+      animgif->stop();
+      image(0);
+      // delete already copied images
+      if (animgif != orig ) {
+        delete animgif;
+      }
+      Fl_Anim_GIF_Image *copied = (Fl_Anim_GIF_Image *)orig->copy(W, H);
+      if (!copied->valid()) { // check success of copy
+        Fl::warning("Fl_Anim_GIF_Image::copy() %d x %d failed", W, H);
+      }
+      else {
+        printf("resized to %d x %d\n", copied->w(), copied->h());
+      }
+      copied->canvas(this, Fl_Anim_GIF_Image::DONT_RESIZE_CANVAS);
+    }
+    window()->cursor(FL_CURSOR_DEFAULT);
+  }
+  static void do_resize_cb(void *d) {
+    Canvas *c = (Canvas *)d;
+    c->do_resize(c->w(), c->h());
+  }
+  void resize(int x, int y, int w, int h) FL_OVERRIDE {
+    Inherited::resize(x, y, w, h);
+    // decouple resize event from actual resize operation
+    // to avoid lockups..
+    Fl::remove_timeout(do_resize_cb, this);
+    Fl::add_timeout(0.1, do_resize_cb, this);
+    window()->cursor(FL_CURSOR_WAIT);
+  }
+};
+
+int main(int argc, char *argv[]) {
+  // setup play parameters from args
+  const char *fileName = 0;
+  bool bilinear = false;
+  bool optimize = false;
+  bool uncache = false;
+  bool debug = false;
+  for (int i = 1; i < argc; i++) {
+    if (!strcmp(argv[i], "-b")) // turn bilinear scaling on
+      bilinear = true;
+    else if (!strcmp(argv[i], "-o")) // turn optimize on
+      optimize = true;
+    else if (!strcmp(argv[i], "-g")) // disable grid
+      draw_grid = false;
+    else if (!strcmp(argv[i], "-u")) // uncache
+      uncache = true;
+    else if (!strcmp(argv[i], "-d")) // debug
+      debug = true;
+    else if (argv[i][0] != '-' && !fileName) {
+      fileName = argv[i];
+    }
+    else if (argv[i][0] == '-') {
+      printf("Invalid argument: '%s'\n", argv[i]);
+      exit(1);
+    }
+  }
+  if (!fileName) {
+    fprintf(stderr, "Test program for animated copy.\n");
+    fprintf(stderr, "Usage: %s fileName [-b]ilinear [-o]ptimize [-g]rid [-u]ncache\n", argv[0]);
+    exit(0);
+  }
+  Fl_Anim_GIF_Image::min_delay = 0.1; // set a minumum delay for playback
+
+  Fl_Double_Window win(640, 480);
+
+  // prepare a canvas for the animation
+  // (we want to show it in the center of the window)
+  Canvas canvas(0, 0, win.w(), win.h());
+  win.resizable(win);
+  win.size_range(1, 1);
+
+  win.end();
+  win.show();
+
+  // create/load the animated gif and start it immediately.
+  // We use the 'DONT_RESIZE_CANVAS' flag here to tell the
+  // animation not to change the canvas size (which is the default).
+  int flags = Fl_Anim_GIF_Image::Fl_Anim_GIF_Image::DONT_RESIZE_CANVAS;
+  if (optimize) {
+    flags |= Fl_Anim_GIF_Image::OPTIMIZE_MEMORY;
+    printf("Using memory optimization (if image supports)\n");
+  }
+  if (debug) {
+    flags |= Fl_Anim_GIF_Image::DEBUG_FLAG;
+  }
+  orig = new Fl_Anim_GIF_Image(/*name_=*/ fileName,
+                             /*canvas_=*/ &canvas,
+                              /*flags_=*/ flags );
+
+  // check if loading succeeded
+  printf("%s: valid: %d frames: %d uncache: %d\n",
+    orig->name(), orig->valid(), orig->frames(), orig->frame_uncache());
+  if (orig->valid()) {
+    win.copy_label(fileName);
+
+    // print information about image optimization
+    int n = 0;
+    for (int i = 0; i < orig->frames(); i++) {
+      if (orig->frame_x(i) != 0 || orig->frame_y(i) != 0) n++;
+    }
+    printf("image has %d optimized frames\n", n);
+
+    Fl_Image::scaling_algorithm(FL_RGB_SCALING_NEAREST);
+    if (bilinear) {
+      Fl_Image::scaling_algorithm(FL_RGB_SCALING_BILINEAR);
+      printf("Using bilinear scaling - can be slow!\n");
+      // NOTE: this can be *really* slow with large sizes, if FLTK
+      //       has to resize on its own without hardware scaling enabled.
+    }
+    orig->frame_uncache(uncache);
+    if (uncache) {
+      printf("Caching disabled - watch cpu load!\n");
+    }
+
+    // set initial size to fit into window
+    double ratio = orig->valid() ? (double)orig->w() / orig->h() : 1;
+    int W = win.w() - 40;
+    int H = (double)W / ratio;
+    printf("original size: %d x %d\n", orig->w(), orig->h());
+    win.size(W, H);
+    Fl::add_handler(events);
+
+    return Fl::run();
+  }
+}
diff --git examples/animgifimage-simple.cxx examples/animgifimage-simple.cxx
new file mode 100644
index 0000000..19f9732
--- /dev/null
+++ examples/animgifimage-simple.cxx
@@ -0,0 +1,40 @@
+//
+//  Minimal program for displaying an animated GIF file
+//  with the Fl_Anim_GIF_Image class.
+//
+#include <FL/Fl_Double_Window.H>
+#include <FL/Fl_Box.H>
+#include <FL/Fl_Anim_GIF_Image.H>
+#include <FL/Fl.H>
+#include <stdio.h>
+
+int main(int argc, char *argv[]) {
+  Fl_Double_Window win(400, 300, "Fl_Anim_GIF_Image demo");
+
+  // prepare a canvas (widget) for the animation
+  Fl_Box canvas(20, 40, win.w()-40, win.h()-80, "Hello from FLTK GIF-animation!");
+  canvas.align(FL_ALIGN_TOP|FL_ALIGN_IMAGE_BACKDROP);
+  canvas.labelsize(20);
+
+  win.resizable(win);
+  win.end();
+  win.show(1, argv);
+
+  // Create and load the animated gif as image
+  // of the `canvas` widget and start it immediately.
+  // We use the `DONT_RESIZE_CANVAS` flag here to tell the
+  // animation *not* to change the canvas size (which is the default).
+  const char *default_image = "../test/pixmaps/fltk_animated.gif";
+  Fl_Anim_GIF_Image animgif(/*name_=*/ argv[1] ? argv[1] : default_image,
+                          /*canvas_=*/ &canvas,
+                           /*flags_=*/ Fl_Anim_GIF_Image::DONT_RESIZE_CANVAS);
+  // resize animation to canvas size
+  animgif.scale(canvas.w(), canvas.h(), /*can_expand*/1, /*proportional*/1);
+
+  // check if loading succeeded
+  printf("%s: ld=%d, valid=%d, frames=%d, size=%dx%d\n",
+    animgif.name(), animgif.ld(), animgif.valid(),
+    animgif.frames(), animgif.canvas_w(), animgif.canvas_h());
+  if (animgif.valid())
+    return Fl::run();
+}
diff --git examples/animgifimage.cxx examples/animgifimage.cxx
new file mode 100644
index 0000000..515b76b
--- /dev/null
+++ examples/animgifimage.cxx
@@ -0,0 +1,303 @@
+//
+//  Test program for displaying animated GIF files using the
+//  Fl_Anim_GIF_Image class.
+//
+#include <FL/Fl_Anim_GIF_Image.H>
+
+#include <FL/Fl_Double_Window.H>
+#include <FL/Fl_File_Chooser.H>
+#include <FL/Fl_Shared_Image.H>
+#include <FL/Fl_Tiled_Image.H>
+#include <stdlib.h>
+
+static int g_good_count = 0, g_bad_count = 0, g_frame_count = 0;
+
+static const Fl_Color BackGroundColor = FL_GRAY; // use e.g. FL_RED to see
+                                                 // transparent parts better
+static const double RedrawDelay = 1./20;         // interval [sec] for forced redraw
+
+static void quit_cb(Fl_Widget* w_, void*) {
+  exit(0);
+}
+
+static void set_title(Fl_Window *win, Fl_Anim_GIF_Image *animgif) {
+  char buf[200];
+  snprintf(buf, sizeof(buf), "%s (%d frames)  %2.2fx", fl_filename_name(animgif->name()),
+          animgif->frames(), animgif->speed());
+  if (animgif->frame_uncache())
+    strcat(buf, " U");
+  win->copy_label(buf);
+  win->copy_tooltip(buf);
+}
+
+static void cb_forced_redraw(void *d) {
+  Fl_Window *win = Fl::first_window();
+  while (win) {
+    if (!win->menu_window())
+      win->redraw();
+    win = Fl::next_window(win);
+  }
+  if (Fl::first_window())
+    Fl::repeat_timeout(RedrawDelay, cb_forced_redraw);
+}
+
+Fl_Window *openFile(const char *name, char *flags, bool close = false) {
+  // determine test options from 'flags'
+  bool uncache = strchr(flags, 'u');
+  char *d = flags - 1;
+  int debug = 0;
+  while ((d = strchr(++d, 'd'))) debug++;
+  bool optimize_mem = strchr(flags, 'm');
+  bool desaturate = strchr(flags, 'D');
+  bool average = strchr(flags, 'A');
+  bool test_tiles = strchr(flags, 'T');
+  bool test_forced_redraw = strchr(flags, 'f');
+  char *r = strchr(flags, 'r');
+  bool resizable = r && !test_tiles;
+  double scale = 1.0;
+  if (r && resizable) scale = atof(r+1);
+  if (scale <= 0.1 || scale > 5)
+    scale = resizable ? 0.7 : 1.0;
+
+  // setup window
+  Fl::remove_timeout(cb_forced_redraw);
+  Fl_Double_Window *win = new Fl_Double_Window(300, 300);
+  win->color(BackGroundColor);
+  if (close)
+    win->callback(quit_cb);
+  printf("Loading '%s'%s%s ... ", name,
+    uncache ? " (uncached)" : "",
+    optimize_mem ? " (optimized)" : "");
+
+  // create a canvas for the animation
+  Fl_Box *canvas = test_tiles ? 0 : new Fl_Box(0, 0, 0, 0); // canvas will be resized by animation
+  Fl_Box *canvas2 = 0;
+  unsigned short gif_flags = debug ? Fl_Anim_GIF_Image::LOG_FLAG : 0;
+  if (debug > 1)
+    gif_flags |= Fl_Anim_GIF_Image::DEBUG_FLAG;
+  if (optimize_mem)
+    gif_flags |= Fl_Anim_GIF_Image::OPTIMIZE_MEMORY;
+
+  // create animation, specifying this canvas as display widget
+  Fl_Anim_GIF_Image *animgif = new Fl_Anim_GIF_Image(name, canvas, gif_flags);
+  bool good( animgif->ld() == 0  && animgif->valid() );
+  printf("%s: %d x %d (%d frames) %s\n",
+    animgif->name(), animgif->w(), animgif->h(), animgif->frames(), good ? "OK" : "ERROR");
+  // for the statistics (when run on testsuite):
+  g_good_count += good;
+  g_bad_count += !good;
+  g_frame_count += animgif->frames();
+
+  win->user_data(animgif); // store address of image (see note in main())
+
+  // exercise the optional tests on the animation
+  animgif->frame_uncache(uncache);
+  if (scale != 1.0) {
+    animgif->resize(scale);
+    printf("TEST: resized %s by %.2f to %d x %d\n", animgif->name(), scale, animgif->w(), animgif->h());
+  }
+  if (average) {
+    printf("TEST: color_average %s\n", animgif->name());
+    animgif->color_average(FL_GREEN, 0.5); // currently hardcoded
+  }
+  if (desaturate) {
+    printf("TEST: desaturate %s\n", animgif->name());
+    animgif->desaturate();
+  }
+  int W = animgif->w();
+  int H = animgif->h();
+  if (animgif->frames()) {
+    if (test_tiles) {
+      // demonstrate a way how to use the animation with Fl_Tiled_Image
+      printf("TEST: use %s as tiles\n", animgif->name());
+      W *= 2;
+      H *= 2;
+      Fl_Tiled_Image *tiled_image = new Fl_Tiled_Image(animgif);
+      Fl_Group *group = new Fl_Group(0, 0, win->w(), win->h());
+      group->image(tiled_image);
+      group->align(FL_ALIGN_INSIDE);
+      animgif->canvas(group, Fl_Anim_GIF_Image::DONT_RESIZE_CANVAS | Fl_Anim_GIF_Image::DONT_SET_AS_IMAGE );
+      win->resizable(group);
+    } else {
+      // demonstrate a way how to use same animation in another canvas simultaneously:
+      // as the current implementation allows only automatic redraw of one canvas..
+      if (test_forced_redraw) {
+        if (W < 400) {
+          printf("TEST: open %s in another animation with application redraw\n", animgif->name());
+          canvas2 = new Fl_Box(W, 0, animgif->w(), animgif->h()); // another canvas for animation
+          canvas2->image(animgif); // is set to same animation!
+          W *= 2;
+          Fl::add_timeout(RedrawDelay, cb_forced_redraw); // force periodic redraw
+        }
+      }
+    }
+    // make window resizable (must be done before show())
+    if (resizable && canvas && !test_tiles) {
+      win->resizable(win);
+    }
+    win->size(W, H); // change to actual size of canvas
+    // start the animation
+    win->end();
+    win->show();
+    win->wait_for_expose();
+    set_title(win, animgif);
+    if (resizable && !test_tiles) {
+      // need to reposition the widgets (have been moved by setting resizable())
+      if (canvas && canvas2) {
+        canvas->resize(0, 0, W/2, canvas->h());
+        canvas2->resize(W/2, 0, W/2, canvas2->h());
+      }
+      else if (canvas) {
+        canvas->resize(0, 0, animgif->canvas_w(), animgif->canvas_h());
+      }
+    }
+    win->init_sizes(); // IMPORTANT: otherwise weird things happen at Ctrl+/- scaling
+  } else {
+    delete win;
+    return 0;
+  }
+  if (debug >=3) {
+    // open each frame in a separate window
+    for (int i = 0; i < animgif->frames(); i++) {
+      char buf[200];
+      snprintf(buf, sizeof(buf), "Frame #%d", i + 1);
+      Fl_Double_Window *win = new Fl_Double_Window(animgif->w(), animgif->h());
+      win->copy_tooltip(buf);
+      win->copy_label(buf);
+      win->color(BackGroundColor);
+      int w = animgif->image(i)->w();
+      int h = animgif->image(i)->h();
+      // in 'optimize_mem' mode frames must be offsetted to canvas
+      int x = (w == animgif->w() && h == animgif->h()) ? 0 : animgif->frame_x(i);
+      int y = (w == animgif->w() && h == animgif->h()) ? 0 : animgif->frame_y(i);
+      Fl_Box *b = new Fl_Box(x, y, w, h);
+      // get the frame image
+      b->image(animgif->image(i));
+      win->end();
+      win->show();
+    }
+  }
+  return win;
+}
+
+#include <FL/filename.H>
+bool openDirectory(const char *dir, char *flags) {
+  dirent **list;
+  int nbr_of_files = fl_filename_list(dir, &list, fl_alphasort);
+  if (nbr_of_files <= 0)
+    return false;
+  int cnt = 0;
+  for (int i = 0; i < nbr_of_files; i++) {
+    char buf[512];
+    const char *name = list[i]->d_name;
+    if (!strcmp(name, ".") || !strcmp(name, "..")) continue;
+    const char *p = strstr(name, ".gif");
+    if (!p) p = strstr(name, ".GIF");
+    if (!p) continue;
+    if (*(p+4)) continue; // is no extension!
+    snprintf(buf, sizeof(buf), "%s/%s", dir, name);
+    if (strstr(name, "debug"))	// hack: when name contains 'debug' open single frames
+      strcat(flags, "d");
+    if (openFile(buf, flags, cnt == 0))
+      cnt++;
+  }
+  return cnt != 0;
+}
+
+static void change_speed(double delta) {
+  Fl_Widget *below = Fl::belowmouse();
+  if (below && below->image()) {
+    Fl_Anim_GIF_Image *animgif = 0;
+    // Q: is there a way to determine Fl_Tiled_Image without using dynamic cast?
+    Fl_Tiled_Image *tiled = dynamic_cast<Fl_Tiled_Image *>(below->image());
+    animgif = tiled ?
+              dynamic_cast<Fl_Anim_GIF_Image *>(tiled->image()) :
+              dynamic_cast<Fl_Anim_GIF_Image *>(below->image());
+    if (animgif && animgif->playing()) {
+      double speed = animgif->speed();
+      if (!delta) speed = 1.;
+      else speed += delta;
+      if (speed < 0.1) speed = 0.1;
+      if (speed > 10) speed = 10;
+      animgif->speed(speed);
+      set_title(below->window(), animgif);
+    }
+  }
+}
+
+static int events(int event) {
+  if (event == FL_SHORTCUT) {
+    if (Fl::event_key() == '+')
+      change_speed(0.1);
+    else if (Fl::event_key() == '-')
+      change_speed(-0.1);
+    else if (Fl::event_key() == '0')
+      change_speed(0);
+    else
+      return 0;
+    return 1;
+  }
+  return 0;
+}
+
+static const char testsuite[] = "testsuite";
+
+int main(int argc, char *argv[]) {
+  fl_register_images();
+  Fl::add_handler(events);
+  char *openFlags = (char *)calloc(1024, 1);
+  if (argc > 1) {
+    // started with argumemts
+    if (strstr(argv[1], "-h")) {
+      printf("Usage:\n"
+             "   -t [directory] [-{flags}] open all files in directory (default name: %s) [with options]\n"
+             "   filename [-{flags}] open single file [with options] \n"
+             "   No arguments open a fileselector\n"
+             "   {flags} can be: d=debug mode, u=uncached, D=desaturated, A=color averaged, T=tiled\n"
+             "                   m=minimal update, r[scale factor]=resize by 'scale factor'\n"
+             "   Use keys '+'/'-/0' to change speed of the active image (belowmouse).\n", testsuite);
+      exit(1);
+    }
+    for (int i = 1; i < argc; i++) {
+      if (argv[i][0] == '-')
+        strcat(openFlags, &argv[i][1]);
+    }
+    if (strchr(openFlags, 't')) { // open all GIF-files in a given directory
+      const char *dir = testsuite;
+      for (int i = 2; i < argc; i++)
+        if (argv[i][0] != '-')
+          dir = argv[i];
+      openDirectory(dir, openFlags);
+      printf("Summary: good=%d, bad=%d, frames=%d\n", g_good_count, g_bad_count, g_frame_count);
+    } else { // open given file(s)
+      for (int i = 1; i < argc; i++)
+        if (argv[i][0] != '-')
+          openFile(argv[i], openFlags, strchr(openFlags, 'd'));
+    }
+  } else {
+    // started without arguments: choose file
+    Fl_GIF_Image::animate = true; // create animated shared .GIF images (e.g. file chooser)
+    while (1) {
+      Fl::add_timeout(0.1, cb_forced_redraw); // animate images in chooser
+      const char *filename = fl_file_chooser("Select a GIF image file","*.{gif,GIF}", NULL);
+      Fl::remove_timeout(cb_forced_redraw);
+      if (!filename)
+        break;
+      Fl_Window *win = openFile(filename, openFlags);
+      Fl::run();
+      // delete last window (which is now just hidden) to test destructors
+      // NOTE: it is essential that *before* doing this also the
+      //       animated image is destroyed, otherwise it will crash
+      //       because it's canvas will be gone.
+      //       In order to keep this demo simple, the adress of the
+      //       Fl_Anim_GIF_Image has been stored in the window's user_data.
+      //       In a real-life application you will probably store
+      //       it somewhere in the window's or canvas' object and destroy
+      //       the image in the window's or canvas' destructor.
+      if (win && win->user_data())
+        delete ((Fl_Anim_GIF_Image *)win->user_data());
+      delete win;
+    }
+  }
+  return Fl::run();
+}
diff --git fluid/Fluid_Image.cxx fluid/Fluid_Image.cxx
index 0340bc7..bc44733 100644
--- fluid/Fluid_Image.cxx
+++ fluid/Fluid_Image.cxx
@@ -30,6 +30,7 @@
 #include <FL/fl_utf8.h>     // fl_fopen()
 #include <FL/Fl_File_Chooser.H>
 #include <FL/Fl_SVG_Image.H>
+#include <FL/Fl_Anim_GIF_Image.H>
 #include "../src/flstring.h"
 
 #include <stdio.h>
@@ -51,6 +52,7 @@ static int image_header_written = 0;
 static int jpeg_header_written = 0;
 static int png_header_written = 0;
 static int gif_header_written = 0;
+static int animated_gif_header_written = 0;
 static int bmp_header_written = 0;
 static int svg_header_written = 0;
 
@@ -134,8 +136,19 @@ void Fluid_Image::write_static(int compressed) {
   if (!img) return;
   const char *idata_name = unique_id(this, "idata", fl_filename_name(name()), 0);
   function_name_ = unique_id(this, "image", fl_filename_name(name()), 0);
-  // TODO: GIF, ICO, BMP
-  if (compressed && strcmp(fl_filename_ext(name()), ".gif")==0) {
+  
+  if (is_animated_gif_) {
+    // Write animated gif image data...
+    write_c("\n");
+    if (animated_gif_header_written != write_number) {
+      write_c("#include <FL/Fl_Anim_GIF_Image.H>\n");
+      animated_gif_header_written = write_number;
+    }
+    write_c("static const unsigned char %s[] =\n", idata_name);
+    size_t nData = write_static_binary("AnimGIF");
+    write_c(";\n");
+    write_initializer("Fl_Anim_GIF_Image", "\"%s\", %s, %d", fl_filename_name(name()), idata_name, nData);
+  } else if (compressed && fl_ascii_strcasecmp(fl_filename_ext(name()), ".gif")==0) {
     // Write gif image data...
     write_c("\n");
     if (gif_header_written != write_number) {
@@ -146,7 +159,7 @@ void Fluid_Image::write_static(int compressed) {
     size_t nData = write_static_binary("GIF");
     write_c(";\n");
     write_initializer("Fl_GIF_Image", "\"%s\", %s, %d", fl_filename_name(name()), idata_name, nData);
-  } else if (compressed && strcmp(fl_filename_ext(name()), ".bmp")==0) {
+  } else if (compressed && fl_ascii_strcasecmp(fl_filename_ext(name()), ".bmp")==0) {
     // Write bmp image data...
     write_c("\n");
     if (bmp_header_written != write_number) {
@@ -198,7 +211,7 @@ void Fluid_Image::write_static(int compressed) {
     write_cdata(img->data()[0], ((img->w() + 7) / 8) * img->h());
     write_c(";\n");
     write_initializer( "Fl_Bitmap", "%s, %d, %d, %d", idata_name, ((img->w() + 7) / 8) * img->h(), img->w(), img->h());
-  } else if (compressed && strcmp(fl_filename_ext(name()), ".jpg")==0) {
+  } else if (compressed && fl_ascii_strcasecmp(fl_filename_ext(name()), ".jpg")==0) {
     // Write jpeg image data...
     write_c("\n");
     if (jpeg_header_written != write_number) {
@@ -209,7 +222,7 @@ void Fluid_Image::write_static(int compressed) {
     size_t nData = write_static_binary("JPEG");
     write_c(";\n");
     write_initializer("Fl_JPEG_Image", "\"%s\", %s, %d", fl_filename_name(name()), idata_name, nData);
-  } else if (compressed && strcmp(fl_filename_ext(name()), ".png")==0) {
+  } else if (compressed && fl_ascii_strcasecmp(fl_filename_ext(name()), ".png")==0) {
     // Write png image data...
     write_c("\n");
     if (png_header_written != write_number) {
@@ -220,7 +233,7 @@ void Fluid_Image::write_static(int compressed) {
     size_t nData = write_static_binary("PNG");
     write_c(";\n");
     write_initializer("Fl_PNG_Image", "\"%s\", %s, %d", fl_filename_name(name()), idata_name, nData);
-  } else if (strcmp(fl_filename_ext(name()), ".svg")==0 || strcmp(fl_filename_ext(name()), ".svgz")==0) {
+  } else if (fl_ascii_strcasecmp(fl_filename_ext(name()), ".svg")==0 || fl_ascii_strcasecmp(fl_filename_ext(name()), ".svgz")==0) {
     bool gzipped = (strcmp(fl_filename_ext(name()), ".svgz") == 0);
     // Write svg image data...
     if (compressed) {
@@ -242,7 +255,7 @@ void Fluid_Image::write_static(int compressed) {
       }
     } else {
       // if FLUID runs from the command line, make sure that the image is not
-      // only loade but also rasterized, so we can write the RGB image data
+      // only loaded but also rasterized, so we can write the RGB image data
       Fl_RGB_Image* rgb_image = NULL;
       Fl_SVG_Image* svg_image = NULL;
       if (img->d()>0)
@@ -279,6 +292,8 @@ void Fluid_Image::write_initializer(const char *type_name, const char *format, .
   va_list ap;
   va_start(ap, format);
   write_c("static Fl_Image *%s() {\n", function_name_);
+  if (is_animated_gif_)
+    write_c("%sFl_GIF_Image::animate = true;\n", indent(1));
   write_c("%sstatic Fl_Image *image = NULL;\n", indent(1));
   write_c("%sif (!image)\n", indent(1));
   write_c("%simage = new %s(", indent(2), type_name);
@@ -292,7 +307,11 @@ void Fluid_Image::write_initializer(const char *type_name, const char *format, .
 void Fluid_Image::write_code(int bind, const char *var, int inactive) {
   /* Outputs code that attaches an image to an Fl_Widget or Fl_Menu_Item.
    This code calls a function output before by Fluid_Image::write_initializer() */
-  if (img) write_c("%s%s->%s%s( %s() );\n", indent(), var, bind ? "bind_" : "", inactive ? "deimage" : "image", function_name_);
+  if (img) {
+    write_c("%s%s->%s%s( %s() );\n", indent(), var, bind ? "bind_" : "", inactive ? "deimage" : "image", function_name_);
+    if (is_animated_gif_)
+      write_c("%s((Fl_Anim_GIF_Image*)(%s()))->canvas(%s, Fl_Anim_GIF_Image::DONT_RESIZE_CANVAS);\n", indent(), function_name_, var);
+  }
 }
 
 void Fluid_Image::write_inline(int inactive) {
@@ -355,11 +374,20 @@ Fluid_Image* Fluid_Image::find(const char *iname) {
   return ret;
 }
 
-Fluid_Image::Fluid_Image(const char *iname) {
+Fluid_Image::Fluid_Image(const char *iname)
+  : is_animated_gif_(false)
+{
   name_ = fl_strdup(iname);
   written = 0;
   refcount = 0;
   img = Fl_Shared_Image::get(iname);
+  if (img && iname) {
+    const char *ext = fl_filename_ext(iname);
+    if (fl_ascii_strcasecmp(ext, ".gif")==0) {
+      int fc = Fl_Anim_GIF_Image::frame_count(iname);
+      if (fc > 0) is_animated_gif_ = true;
+    }
+  }
   function_name_ = NULL;
 }
 
diff --git fluid/Fluid_Image.h fluid/Fluid_Image.h
index 4845160..deb9ecf 100644
--- fluid/Fluid_Image.h
+++ fluid/Fluid_Image.h
@@ -24,6 +24,7 @@
 #include <FL/Fl_Shared_Image.H>
 
 class Fluid_Image {
+  bool is_animated_gif_;
   const char *name_;
   int refcount;
   Fl_Shared_Image *img;
diff --git fluid/about_panel.cxx fluid/about_panel.cxx
index 61fcdb6..b01616e 100644
--- fluid/about_panel.cxx
+++ fluid/about_panel.cxx
@@ -21,236 +21,127 @@ void show_help(const char *name);
 
 Fl_Double_Window *about_panel=(Fl_Double_Window *)0;
 
-#include <FL/Fl_Pixmap.H>
-static const char *idata_fluid[] = {
-"96 96 32 1",
-" \tc None",
-".\tc #000100",
-"+\tc #031F3F",
-"@\tc #00366C",
-"#\tc #2E302D",
-"$\tc #0058AC",
-"%\tc #0060BF",
-"&\tc #4E504D",
-"*\tc #14659F",
-"=\tc #006DDC",
-"-\tc #2C7087",
-";\tc #0080FF",
-">\tc #407B74",
-",\tc #0F85F9",
-"\'\tc #268CCD",
-")\tc #7C7E7B",
-"!\tc #2D92EC",
-"~\tc #4498A9",
-"{\tc #2F94FE",
-"]\tc #5BA18C",
-"^\tc #6BA674",
-"/\tc #7DAD62",
-"(\tc #93BD53",
-"_\tc #A4A6A2",
-":\tc #6CB6FF",
-"<\tc #ABCC3F",
-"[\tc #C4DA2A",
-"}\tc #CACCC9",
-"|\tc #DCE913",
-"1\tc #BBDEFF",
-"2\tc #FDFE00",
-"3\tc #FDFFFC",
-"                                                                             \
-                   ",
-"                                                                             \
-                   ",
-"                                                                             \
-                   ",
-"                                           ...........                       \
-                   ",
-"                                     ......................                  \
-                   ",
-"                                    ........................                 \
-                   ",
-"                                    ........#&#&#&#&##......                 \
-                   ",
-"                                    ....)__}33333333333}_...                 \
-                   ",
-"                                    ...&33333333333333333...                 \
-                   ",
-"                                    ...#33311133333333333...                 \
-                   ",
-"                                    ...&33!,{,;:333333333...                 \
-                   ",
-"                                    ...&3:,{{{{,13333333}...                 \
-                   ",
-"                                    ...&3!{{!{{,13333333}...                 \
-                   ",
-"                                    ...&3:!{{!{;13333333}...                 \
-                   ",
-"                                    ...&3{{{{{{;133333333...                 \
-                   ",
-"                                    ...&31,{{{;,33333333}...                 \
-                   ",
-"                                    ...&331{{{:133333333}...                 \
-                   ",
-"                                    ...&3333333333333333_...                 \
-                   ",
-"                                    ...&3333333333333333}...                 \
-                   ",
-"                                    ...&3333333333333333_...                 \
-                   ",
-"                                    ...&3333333333333333}...                 \
-                   ",
-"                                    ...&3333333333333333_...                 \
-                   ",
-"                                    ...&3333333333333333}...                 \
-                   ",
-"                                    ...&3333333333333333_...                 \
-                   ",
-"                                    ...&3333333331!,,;:3}...                 \
-                   ",
-"                                    ...&333333333{{{{{;:_...                 \
-                   ",
-"                                    ...&333333331,{!{!{{}...                 \
-                   ",
-"                                    ...&333333331{{{{{{,_...                 \
-                   ",
-"                                    ...)333333331{{!{{{{_...                 \
-                   ",
-"                                    ...)333333333{{{!{;:_...                 \
-                   ",
-"                                    ...)3333333331{;;;:3_...                 \
-                   ",
-"                                    ...)3333333333331333_...                 \
-                   ",
-"                                    ...)3333333333333333_...                 \
-                   ",
-"                                    ...)3333333333333333_...                 \
-                   ",
-"                                    ..._3333333333333333_...                 \
-                   ",
-"                                    ..._3333333333333333_...                 \
-                   ",
-"                                    ..._3333333333333333_...                 \
-                   ",
-"                                    ..._3333333333333333}....                \
-                   ",
-"                                   ...._33333333333333333#...                \
-                   ",
-"                                  ....&333333333333333333_....               \
-                   ",
-"                                 ....&33333333333333333333)....              \
-                   ",
-"                                 ....333333333333333333333}&....             \
-                   ",
-"                                ...._33333333333333333333333....             \
-                   ",
-"                               ....&333333333331:11333333333_....            \
-                   ",
-"                              ....#33333333333:,,,;:333333333&....           \
-                   ",
-"                              ....}3333333333:,!{{{;1333333333&....          \
-                   ",
-"                             ....}33333333333{{{!{{,!3333333333....          \
-                   ",
-"                            ....)333333333333{{{{!{{{3333333333_....         \
-                   ",
-"                           ....#3333333333333!{{{{{,:33333333333&....        \
-                   ",
-"                           ...._33333333333331{{!{,;1333333333333#....       \
-                   ",
-"                          ...._333333333333333:;,;,13333333333333_....       \
-                   ",
-"                         ...._333333333333333333113333333333333333_....      \
-                   ",
-"                        ....&33333333333333333333333333331::1333333&....     \
-                   ",
-"                        ...._333333333333333333333333333{,{{;{133333#...     \
-                   ",
-"                       ...._3333333333333333333333333331,{!{{,:33333}....    \
-                   ",
-"                      ....&3333333333133333333333333333:{{{{{{:333333)....   \
-                   ",
-"                      ...#333333331{,,;:333333333333333:{!{!{{:3333333&....  \
-                   ",
-"                     ....}33333333,{{{{;:333333333333331,{!{{;:33333333#...  \
-                   ",
-"                    ...._333333331,!{!{{,333333333333333{,{{;{1333333333.... \
-                   ",
-"                   ....&3333333331{{{{{{{3333333333333333::::33333333333)....\
-                   ",
-"                  ....+!:::::::::{{{{!{{;::::::::::::::::::::::::::!:::::+...\
-.                  ",
-"                  ...+=;;;;;;;;;;;;{{{{;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;=...\
-.                  ",
-"                 ....%;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;%..\
-..                 ",
-"                ....@;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;$.\
-...                ",
-"                ...+%;;;;;;!!!;;;;;,;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;!=;;;+\
-....               ",
-"               ....%;;;;;!([<|^~]^([%;;;;;;;;;;;;;;;;;,(<\'=;;;;;;;!^/<[|\'=;\
-;=+...               ",
-"              ....$;;;;;\'|2>]22<|22[%=;;;;;;;;;;;;;;;;^22[%=;;;;;;!][22|%=;;\
-;$....              ",
-"             ....@;;;;;;[2[%^2|*[22(%=;;;;;;;;;;;;;;;,/22|$=;;;;;;;;<22<%=;;;\
-;$....             ",
-"            ....+=;;;;;~22^$%]~$|22>%=;;;;;;;;;;;;;;;;\'||^%=;;;;;;;,[22^$=;;\
-;;;+....            ",
-"            ....%;;;;;,[22-%===\'22|*==;;;;;;;;;;;;;;;;;;=%=;;;;;;;;\'22|*%=;\
-;;;;=+...            ",
-"           ....$;;;;;;!22|$%;,;^22<$=;;;;;;;;;;;;;;;;;;===;;;;;;;;;^22|$==;;;\
-;;;%....           ",
-"          ....@;;;;;\'](22[^]=;;<22^$==!~]/~=;!]]~;;;;{\'~]==;;;;~<<]<22($=;;\
-;;;;;;@....          ",
-"         ....@;;;;;;]<|22|[<%;!|22-%\'[2222*=;/22(%;~|222(=;;;!<2|^[222>$=;;;\
-;;;;;;+....         ",
-"         ....=;;;;;;;,[22>$===~22|$==,[22[%=;[22]%=,!|22]%=;![2|*%]22|*==;;;;\
-;;;;;%+...         ",
-"        ....@;;;;;;;;!|22*$=;;/22($=;,[22/$=\'222*%=;!|22-%;;<22>%=]22[$%;;;;\
-;;;;;;;=....        ",
-"       ....@;;;;;;;;;~22[*==;;[22>%=;\'22|-%,^22[$=;,~22[$%;]22<$%=(22/$=;;;;\
-;;;;;;;;@....       ",
-"      ....+;;;;;;;;;;^22<$=;;!222*$=;]22[$==[22/$=;;(22/$=![22]$=;|22-%=;;;;;\
-;;;;;;;;+...       ",
-"      ....;;;;;;;;;;;<22^%=;;]22[$=;;(22/$=~222-%=;;[22>%=]22|$%;~22|$==;;;;;\
-;;;;;;;;;....      ",
-"     ....%;;;;;;;;;;;|22-%=;;(22/$=;{|22-%=<22|$%;;\'22|*%;<22<$==(22<$=;=;;;\
-;;;;;;;;;;$....     ",
-"    ....+;;;;;;;;;;;!222$==;,|22>%=;~22|$=]|22($=;;]22[$%,|22^%=!|22^$=;;;;;;\
-;;;;;;;;;;@....    ",
-"   ....+=;;;;;;;;;;;~22[$%;;\'22|*-/;]22($*[<22^$^=;(22/$(-222>$=(222->~;;;;;\
-;;;;;;;;;;;=+....   ",
-"   ...+;;;;;;;;;;;;;(22/$=;;]22|*<\'=(22/*[~[22>(]=;|22>//=|22/$^(|2|-[%=;;;;\
-;;;;;;;;;;;;=....   ",
-"  ....$;;;;;;;;;;;;;<22>%=;;]222|>==(222|^=|22|<%=;|222<%=(222|<-222|-==;;;;;\
-;;;;;;;;;;;;$....  ",
-" ....@;;;;;;;;;;;;;!|2|$=;;;\'[2[>%=;\'|2[]%=/2|/$==;^2|(*%=!(2|($%<2[-%=;;;;\
-;;;;;;;;;;;;;;;@.... ",
-"....@;;;;;;;;;;;;;;\'22($%;;;;=%%==;;;=%%%==;=%%==;;;=%===;;==%%====%%=,;;;;;\
-;;;;;;;;;;;;;;;+... ",
-"...+=;;;;;;;;;;!\'=,]22-%=;;;;;;==;=;;;===;=;;===;;;;;===;;;;=;=,;;,=;=;;;;;;\
-;;;;;;;;;;;;;;;=....",
-"...+;;;;;;;;;;;[2^=<2<$==;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\
-;;;;;;;;;;;;;;;+...",
-"...+;;;;;;;;;;;22(\'2|*%=;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\
-;;;;;;;;;;;;;;;;+...",
-"...+;;;;;;;;;;;^|<[[-%=;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\
-;;;;;;;;;;;;;;;+...",
-"...+;;;;;;;;;;;;*~*%===;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\
-;;;;;;;;;;;;;;;@...",
-"...+;;;;;;;;;;;;;====;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\
-;;;;;;;;;;;;;;;+...",
-"....$=;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\
-;;;;;;;;;;;;;=$....",
-" .....+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\
-+++++++++++++..... ",
-" ............................................................................\
-.................  ",
-"  ...........................................................................\
-.................  ",
-"     ........................................................................\
-..............     "
-};
+#include <FL/Fl_Anim_GIF_Image.H>
+static const unsigned char idata_fluid[] =
+{71,73,70,56,57,97,96,0,96,0,132,31,0,0,1,0,3,31,63,46,48,45,0,54,108,78,80,
+77,64,123,116,124,126,123,125,173,98,107,166,116,171,204,63,220,233,19,253,254,
+0,196,218,42,147,189,83,20,101,159,44,112,135,0,88,172,0,96,191,0,109,220,91,
+161,140,68,152,169,38,140,205,0,128,255,15,133,249,45,146,236,47,148,254,108,
+182,255,164,166,162,187,222,255,202,204,201,253,255,252,0,0,0,33,255,11,78,69,
+84,83,67,65,80,69,50,46,48,3,1,0,0,0,33,249,4,5,10,0,31,0,44,0,0,0,0,96,0,96,0,
+0,5,254,224,39,142,100,105,158,104,170,174,108,235,190,112,44,207,116,109,223,
+120,110,2,124,239,3,186,160,238,71,36,14,34,61,161,18,86,244,13,32,78,139,5,201,
+91,90,87,63,143,118,171,201,112,184,25,11,180,122,45,239,120,219,180,135,147,
+241,174,219,223,164,121,46,234,169,211,108,111,87,163,149,211,205,118,119,96,28,
+93,95,28,3,100,127,101,129,130,90,95,107,26,93,22,136,138,128,104,120,28,143,
+90,124,121,97,80,149,139,151,142,109,164,111,134,153,99,160,87,140,155,145,153,
+142,142,113,137,170,66,172,130,108,90,112,126,180,65,182,119,93,163,26,136,64,
+188,181,162,183,112,111,25,169,197,189,199,119,121,143,108,204,205,57,190,120,
+25,124,91,28,187,213,54,215,120,119,221,222,52,224,141,125,179,228,229,207,231,
+105,227,234,49,230,231,239,240,47,242,141,244,245,45,247,130,249,250,88,236,218,
+161,35,246,47,222,179,78,110,196,165,43,184,239,24,194,100,106,252,49,60,193,
+232,33,41,77,3,39,50,17,101,17,226,22,137,26,73,84,36,69,50,225,199,133,33,254,
+81,140,44,233,49,99,74,22,172,186,148,212,230,14,229,203,18,172,44,98,116,121,
+51,133,47,66,26,118,158,36,216,147,98,64,129,32,95,242,83,72,180,40,206,163,
+237,124,56,61,3,245,156,128,164,19,125,16,16,40,104,131,212,169,31,180,114,109,
+100,224,171,83,31,99,149,37,236,64,192,236,77,31,27,198,178,204,48,180,105,72,
+177,92,59,62,242,138,213,155,15,1,105,231,182,217,210,182,111,51,31,29,2,11,78,
+83,216,38,60,196,105,61,8,166,91,211,49,57,31,6,34,171,37,185,147,175,229,195,
+61,0,107,222,108,146,177,219,199,61,226,142,134,228,202,234,105,191,169,87,107,
+246,108,23,52,15,213,178,35,211,86,135,55,183,230,198,181,85,193,245,189,250,
+234,103,58,195,183,93,36,126,174,195,235,74,189,37,179,100,78,246,249,159,208,
+216,88,10,165,238,1,56,45,200,91,38,115,23,100,60,120,168,219,106,196,143,103,
+106,126,73,244,92,139,215,171,41,107,184,134,143,0,24,34,233,143,116,161,191,
+255,11,251,5,40,224,128,4,6,24,128,117,198,0,254,16,128,4,82,52,232,224,131,16,
+70,40,225,132,16,74,128,224,16,61,68,64,225,134,28,118,8,33,21,199,217,215,195,
+0,30,150,104,226,132,99,132,56,67,15,1,104,248,32,6,48,62,120,193,137,52,98,
+192,160,20,7,214,215,16,15,46,54,136,65,3,12,36,160,0,2,20,76,128,0,144,61,122,
+120,65,3,9,84,112,227,139,8,28,144,0,3,10,56,41,133,4,57,182,183,98,15,16,60,88,
+129,2,11,20,48,193,2,11,8,73,38,3,17,60,217,33,2,103,166,9,33,6,19,48,64,166,2,
+110,74,145,162,150,27,241,64,226,131,114,162,201,166,2,14,200,185,64,3,117,118,
+120,193,1,115,66,160,166,131,9,144,153,64,161,98,92,104,15,139,139,82,64,38,2,
+16,68,48,1,5,16,128,25,38,164,28,126,57,36,168,82,92,32,40,166,139,102,137,39,
+76,25,202,40,232,3,105,74,32,65,5,115,58,32,43,141,18,144,42,5,173,11,0,10,42,
+150,58,62,197,67,151,47,38,170,225,140,108,150,169,40,174,183,74,152,172,2,138,
+46,58,133,164,42,254,68,225,96,5,19,52,112,38,2,19,220,216,232,2,168,74,128,65,
+145,7,80,192,32,156,155,58,152,65,5,69,54,43,5,5,9,36,48,193,183,13,44,27,225,
+48,42,10,11,192,158,14,206,235,169,2,65,106,136,129,167,176,86,32,40,153,182,
+90,128,232,160,26,82,224,233,160,79,98,208,232,144,7,23,96,111,132,170,202,224,
+131,180,22,152,74,166,197,178,74,96,105,175,209,122,188,0,154,12,10,58,65,154,
+23,12,76,230,202,231,202,233,235,152,189,218,202,177,20,17,100,105,144,158,18,
+186,188,128,3,246,46,92,47,131,38,31,160,40,175,63,187,233,51,172,82,124,91,64,
+154,52,51,144,233,134,22,246,101,109,132,35,51,96,179,20,130,62,205,32,175,10,
+192,122,65,178,82,19,157,245,212,52,39,144,169,4,218,46,96,244,205,14,226,187,
+234,125,20,38,171,246,141,24,144,249,243,178,81,71,43,232,219,82,180,253,54,6,
+42,47,75,176,174,16,234,204,42,15,27,126,139,64,157,125,223,40,184,162,35,47,0,
+235,141,93,67,109,172,5,35,67,235,238,134,254,146,250,144,100,132,135,75,78,38,
+224,25,148,254,45,180,46,130,237,128,134,223,222,205,182,163,203,194,237,224,
+157,62,177,184,97,222,100,70,219,177,167,94,115,158,168,4,19,120,58,180,20,125,
+71,112,129,167,143,139,203,252,197,29,202,109,20,15,11,110,120,118,235,181,62,
+112,128,5,52,215,27,168,227,16,32,192,224,228,13,60,160,55,200,109,91,94,0,5,38,
+2,59,142,238,28,78,126,35,205,128,54,57,187,219,129,82,208,117,3,221,90,0,252,
+1,7,144,128,167,140,118,36,48,133,13,101,237,59,141,15,136,213,184,143,65,78,
+111,10,40,128,172,210,55,36,1,206,233,81,12,122,24,6,41,152,0,243,205,233,1,159,
+235,16,238,234,48,162,14,185,204,115,13,50,216,201,130,247,165,147,193,12,81,10,
+120,27,131,254,212,128,215,137,75,91,10,168,87,4,26,197,128,203,209,104,18,82,
+185,218,134,120,165,67,7,229,42,86,13,58,34,18,143,232,174,92,133,236,74,76,12,
+89,4,88,246,67,11,232,140,82,20,194,128,147,46,64,51,31,62,232,86,79,122,148,
+226,141,158,104,196,207,49,72,2,51,234,216,25,171,104,129,170,253,32,0,27,146,
+147,248,26,37,59,54,218,241,142,86,36,2,28,41,68,166,6,208,202,87,182,195,163,
+32,39,164,42,234,109,8,1,10,152,82,15,17,55,200,70,74,168,144,10,226,144,3,40,
+96,195,16,58,242,146,247,210,99,135,66,22,72,76,98,18,146,0,128,158,39,71,121,
+34,9,224,238,141,1,72,165,42,87,201,202,86,186,242,149,176,140,165,44,103,201,
+202,31,144,176,9,184,204,165,46,119,201,203,94,246,82,36,190,12,166,48,135,73,
+76,34,76,175,152,200,76,102,49,75,16,2,0,33,249,4,5,10,0,31,0,44,30,0,4,0,38,0,
+57,0,0,5,254,224,39,142,31,96,158,104,170,2,100,235,174,112,236,206,174,103,
+223,56,158,210,60,205,113,185,91,111,152,201,136,56,25,100,50,232,25,246,126,71,
+227,143,233,57,57,121,72,13,105,169,51,93,105,154,204,141,243,225,222,172,95,
+151,70,227,209,126,128,65,116,186,85,92,127,168,115,44,156,218,204,147,236,111,
+100,83,113,94,126,128,69,101,98,132,44,115,124,124,114,126,45,123,57,145,88,102,
+56,149,60,110,153,156,157,158,159,160,161,162,149,142,120,145,165,76,164,168,
+148,167,171,152,173,174,54,170,177,125,126,180,178,176,177,179,76,28,107,26,100,
+163,34,97,69,69,192,162,74,196,197,163,201,201,155,160,204,196,206,159,208,117,
+163,195,204,198,160,200,209,66,161,189,35,147,184,78,183,169,227,228,229,78,189,
+191,231,226,62,208,225,186,154,208,108,231,150,212,240,174,238,247,236,181,36,
+219,201,217,60,253,35,22,80,224,187,96,111,0,130,226,199,176,161,67,90,219,128,
+61,196,49,144,204,68,27,3,139,93,244,144,49,137,175,143,32,67,138,244,149,225,
+130,201,147,38,3,51,132,0,0,33,249,4,5,10,0,31,0,44,34,0,7,0,34,0,54,0,0,5,202,
+224,39,138,94,105,158,168,57,174,236,154,190,104,43,143,112,237,205,179,13,227,
+178,254,242,45,95,10,200,18,198,136,52,163,10,73,82,150,152,77,39,116,74,141,42,
+167,206,39,52,123,171,122,191,224,176,120,76,46,155,207,232,244,151,195,230,156,
+57,154,140,60,227,94,107,52,245,185,190,62,229,232,229,113,127,114,85,129,130,
+134,83,1,126,130,23,134,25,136,138,127,140,135,80,137,141,134,124,76,144,127,
+112,43,28,90,80,154,114,158,30,109,163,93,125,123,58,72,92,75,64,172,39,106,177,
+76,175,63,69,175,112,119,152,46,172,133,162,61,92,161,190,182,89,141,26,65,92,
+197,199,196,134,198,195,78,189,115,186,31,172,193,205,100,112,209,68,144,120,
+102,208,116,180,90,193,25,26,224,93,222,114,229,34,231,25,233,31,25,23,240,241,
+240,119,244,245,246,247,244,33,0,33,249,4,5,10,0,31,0,44,36,0,13,0,26,0,48,0,0,
+5,196,224,39,138,156,102,114,99,170,174,100,230,186,26,43,143,220,107,163,243,
+26,104,182,157,235,188,158,235,167,218,9,135,196,84,77,24,75,166,130,55,167,
+106,153,209,224,164,88,44,103,123,205,150,162,217,207,177,155,164,246,178,208,
+51,54,237,211,30,51,225,177,103,78,247,252,190,175,79,189,46,219,115,57,123,
+124,44,129,132,123,125,133,136,42,0,139,136,137,41,139,0,141,30,93,100,31,144,
+141,102,46,28,1,131,133,98,71,14,157,132,153,47,26,156,41,152,111,149,57,164,
+154,104,76,97,164,171,101,39,78,84,86,97,31,108,25,128,146,83,71,26,146,118,79,
+111,194,42,187,46,198,196,71,202,52,114,190,191,54,193,205,74,38,86,194,216,217,
+218,219,136,102,171,194,178,43,146,173,188,42,227,170,199,214,234,214,25,23,238,
+239,238,112,41,33,0,33,249,4,5,10,0,31,0,44,38,0,5,0,21,0,56,0,0,5,232,224,39,
+142,80,52,158,104,202,101,25,151,190,98,166,105,172,11,167,44,171,125,156,125,
+139,171,26,173,245,27,245,56,195,93,17,185,11,250,96,131,97,203,89,20,205,106,
+181,170,199,99,67,102,127,219,174,231,11,3,132,129,231,159,57,204,217,186,223,
+111,145,185,7,175,199,81,109,207,76,249,91,105,182,82,79,48,109,121,71,85,64,68,
+135,39,52,127,112,76,26,130,136,132,133,57,58,47,121,34,92,149,57,28,1,153,118,
+122,155,57,3,159,118,82,155,164,31,160,161,162,25,169,171,65,155,26,175,171,167,
+53,158,170,171,154,58,79,186,160,138,192,193,194,39,190,118,55,73,160,0,0,120,
+162,121,111,2,56,173,117,27,203,70,173,45,117,204,173,206,110,218,205,198,39,
+215,191,39,177,156,197,110,73,96,231,235,117,71,189,190,167,124,240,162,74,186,
+229,149,46,246,215,249,176,251,185,160,247,56,85,177,37,67,81,188,17,231,220,17,
+99,199,176,225,186,61,16,35,74,220,19,2,0,59};
 static Fl_Image *image_fluid() {
-  static Fl_Image *image = new Fl_Pixmap(idata_fluid);
+  Fl_GIF_Image::animate = true;
+  static Fl_Image *image = NULL;
+  if (!image)
+    image = new Fl_Anim_GIF_Image("fluid.animated.gif", idata_fluid, 2545);
   return image;
 }
 
@@ -275,6 +166,8 @@ Fl_Double_Window* make_about_panel() {
     about_panel->hotspot(about_panel);
     { Fl_Box* o = new Fl_Box(10, 10, 115, 120);
       o->image( image_fluid() );
+      ((Fl_Anim_GIF_Image*)(image_fluid()))->canvas(o, Fl_Anim_GIF_Image::DONT_RESIZE_CANVAS);
+      ((Fl_Anim_GIF_Image*)(o->image()))->speed(0.5f);
     } // Fl_Box* o
     { Fl_Box* o = new Fl_Box(135, 10, 205, 75, "FLTK User\nInterface Designer\nVersion x.x.x");
       o->color((Fl_Color)12);
diff --git fluid/about_panel.fl fluid/about_panel.fl
index 2d465a8..82c8ce6 100644
--- fluid/about_panel.fl
+++ fluid/about_panel.fl
@@ -36,11 +36,12 @@ if (!cbuf[0]) {
 }} {}
   Fl_Window about_panel {
     label {About FLUID} open
-    xywh {449 217 345 180} type Double color 50 selection_color 47 hide hotspot
-    code0 {\#include "../src/flstring.h"} non_modal
+    xywh {449 217 345 180} type Double color 50 selection_color 47 hotspot
+    code0 {\#include "../src/flstring.h"} non_modal visible
   } {
-    Fl_Box {} {
-      image {icons/fluid-96.xpm} xywh {10 10 115 120}
+    Fl_Box {} {selected
+      image {icons/fluid.animated.gif} compress_image 1 xywh {10 10 115 120}
+      code0 {((Fl_Anim_GIF_Image*)(o->image()))->speed(0.5f);}
     }
     Fl_Box {} {
       label {FLTK User
@@ -59,7 +60,7 @@ Version x.x.x}
     }
     Fl_Button {} {
       label {View License...}
-      callback {show_help("license.html");} selected
+      callback {show_help("license.html");}
       xywh {115 145 123 25} labelcolor 136
     }
     Fl_Return_Button {} {
diff --git fluid/icons/fluid.animated.gif fluid/icons/fluid.animated.gif
new file mode 100644
index 0000000..c213991
Binary files /dev/null and fluid/icons/fluid.animated.gif differ
diff --git src/CMakeLists.txt src/CMakeLists.txt
index 6567d21..daa66c2 100644
--- src/CMakeLists.txt
+++ src/CMakeLists.txt
@@ -471,6 +471,7 @@ set (IMGCPPFILES
   Fl_BMP_Image.cxx
   Fl_File_Icon2.cxx
   Fl_GIF_Image.cxx
+  Fl_Anim_GIF_Image.cxx
   Fl_Help_Dialog.cxx
   Fl_ICO_Image.cxx
   Fl_JPEG_Image.cxx
diff --git src/Fl_Anim_GIF_Image.cxx src/Fl_Anim_GIF_Image.cxx
new file mode 100644
index 0000000..e0196cf
--- /dev/null
+++ src/Fl_Anim_GIF_Image.cxx
@@ -0,0 +1,1279 @@
+//
+// Fl_Anim_GIF_Image class for the Fast Light Tool Kit (FLTK).
+//
+// Copyright 2016-2023 by Christian Grabner <wcout@gmx.net>.
+//
+// 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:
+//
+//     https://www.fltk.org/COPYING.php
+//
+// Please see the following page on how to report bugs and issues:
+//
+//     https://www.fltk.org/bugs.php
+//
+
+#include <FL/Fl.H>
+#include <FL/Fl_GIF_Image.H>
+#include <FL/Fl_Shared_Image.H>
+#include <FL/Fl_Graphics_Driver.H>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <math.h> // lround()
+
+#include <FL/Fl_Anim_GIF_Image.H>
+
+/** \class Fl_Anim_GIF_Image
+
+ The Fl_Anim_GIF_Image class supports loading, caching, and drawing of animated
+ Compuserve GIF<SUP>SM</SUP> images.
+
+ The class loads all images contained in the file and animates them by cycling
+ through them as defined by the delay times in the image file.
+
+ The user must supply an FLTK widget as "container" in order to see the
+ animation by specifying it in the constructor or later using the
+ canvas() method.
+*/
+
+/*static*/
+bool Fl_GIF_Image::animate = false;
+
+
+///////////////////////////////////////////////////////////////////////
+//  Internal helper classes/structs
+///////////////////////////////////////////////////////////////////////
+
+class Fl_Anim_GIF_Image::FrameInfo {
+  friend class Fl_Anim_GIF_Image;
+
+  enum Transparency {
+    T_NONE = 0xff,
+    T_FULL = 0
+  };
+
+  struct RGBA_Color {
+    uchar r, g, b, alpha;
+    RGBA_Color(uchar r = 0, uchar g = 0, uchar b = 0, uchar a = T_NONE) :
+      r(r), g(g), b(b), alpha(a) {}
+  };
+
+  enum Dispose {
+    DISPOSE_UNDEF = 0,
+    DISPOSE_NOT = 1,
+    DISPOSE_BACKGROUND = 2,
+    DISPOSE_PREVIOUS = 3
+  };
+
+  struct GifFrame {
+    GifFrame() :
+      rgb(0),
+      scalable(0),
+      average_color(FL_BLACK),
+      average_weight(-1),
+      desaturated(false),
+      x(0),
+      y(0),
+      w(0),
+      h(0),
+      delay(0),
+      dispose(DISPOSE_UNDEF),
+      transparent_color_index(-1) {}
+    Fl_RGB_Image *rgb;                // full frame image
+    Fl_Shared_Image *scalable;        // used for hardware-accelerated scaling
+    Fl_Color average_color;           // last average color
+    float average_weight;             // last average weight
+    bool desaturated;                 // flag if frame is desaturated
+    unsigned short x, y, w, h;        // frame original dimensions
+    double delay;                     // delay (already converted to ms)
+    Dispose dispose;                  // disposal method
+    int transparent_color_index;      // needed for dispose()
+    RGBA_Color transparent_color;     // needed for dispose()
+  };
+
+  FrameInfo(Fl_Anim_GIF_Image *anim) :
+    anim(anim),
+    valid(false),
+    frames_size(0),
+    frames(0),
+    loop_count(1),
+    loop(0),
+    background_color_index(-1),
+    canvas_w(0),
+    canvas_h(0),
+    desaturate(false),
+    average_color(FL_BLACK),
+    average_weight(-1),
+    scaling((Fl_RGB_Scaling)0),
+    debug_(0),
+    optimize_mem(false),
+    offscreen(0) {}
+  ~FrameInfo();
+  void clear();
+  void copy(const FrameInfo& fi);
+  double convert_delay(int d) const;
+  int debug() const { return debug_; }
+  bool load(const char *name, const unsigned char *data, size_t length);
+  bool push_back_frame(const GifFrame &frame);
+  void resize(int W, int H);
+  void scale_frame(int frame);
+  void set_frame(int frame);
+private:
+  Fl_Anim_GIF_Image *anim;          // a pointer to the Image (only needed for name())
+  bool valid;                       // flag if valid data
+  int frames_size;                  // number of frames stored in 'frames'
+  GifFrame *frames;                 // "vector" for frames
+  int loop_count;                   // loop count from file
+  int loop;                         // current loop count
+  int background_color_index;       // needed for dispose()
+  RGBA_Color background_color;      // needed for dispose()
+  GifFrame frame;                   // current processed frame
+  int canvas_w;                     // width of GIF from header
+  int canvas_h;                     // height of GIF from header
+  bool desaturate;                  // flag if frames should be desaturated
+  Fl_Color average_color;           // color for color_average()
+  float average_weight;             // weight for color_average (negative: none)
+  Fl_RGB_Scaling scaling;           // saved scaling method for scale_frame()
+  int debug_;                       // Flag for debug outputs
+  bool optimize_mem;                // Flag to store frames in original dimensions
+  uchar *offscreen;                 // internal "offscreen" buffer
+private:
+private:
+  void dispose(int frame_);
+  void on_frame_data(Fl_GIF_Image::GIF_FRAME &gf);
+  void on_extension_data(Fl_GIF_Image::GIF_FRAME &gf);
+  void set_to_background(int frame_);
+};
+
+
+#define LOG(x) if (debug()) printf x
+#define DEBUG(x) if (debug() >= 2) printf x
+#ifndef LOG
+  #define LOG(x)
+#endif
+#ifndef DEBUG
+  #define DEBUG(x)
+#endif
+
+
+//
+// helper class FrameInfo implementation
+//
+
+Fl_Anim_GIF_Image::FrameInfo::~FrameInfo() {
+  clear();
+}
+
+
+void Fl_Anim_GIF_Image::FrameInfo::clear() {
+  // release all allocated memory
+  while (frames_size-- > 0) {
+    if (frames[frames_size].scalable)
+      frames[frames_size].scalable->release();
+    delete frames[frames_size].rgb;
+  }
+  delete[] offscreen;
+  offscreen = 0;
+  free(frames);
+  frames = 0;
+  frames_size = 0;
+}
+
+
+double Fl_Anim_GIF_Image::FrameInfo::convert_delay(int d) const {
+  if (d <= 0)
+    d = loop_count != 1 ? 10 : 0;
+  return (double)d / 100;
+}
+
+
+void Fl_Anim_GIF_Image::FrameInfo::copy(const FrameInfo& fi) {
+  // copy from source
+  for (int i = 0; i < fi.frames_size; i++) {
+    if (!push_back_frame(fi.frames[i])) {
+      break;
+    }
+    double scale_factor_x = (double)canvas_w / (double)fi.canvas_w;
+    double scale_factor_y = (double)canvas_h / (double)fi.canvas_h;
+    if (fi.optimize_mem) {
+      frames[i].x = lround(fi.frames[i].x * scale_factor_x);
+      frames[i].y = lround(fi.frames[i].y * scale_factor_y);
+      int new_w = (int)lround(fi.frames[i].w * scale_factor_x);
+      int new_h = (int)lround(fi.frames[i].h * scale_factor_y);
+      frames[i].w = new_w;
+      frames[i].h = new_h;
+    }
+    // just copy data 1:1 now - scaling will be done adhoc when frame is displayed
+    frames[i].rgb = (Fl_RGB_Image *)fi.frames[i].rgb->copy();
+    frames[i].scalable = 0;
+  }
+  optimize_mem = fi.optimize_mem;
+  scaling = Fl_Image::RGB_scaling(); // save current scaling mode
+  loop_count = fi.loop_count; // .. and the loop_count!
+}
+
+
+void Fl_Anim_GIF_Image::FrameInfo::dispose(int frame) {
+  if (frame < 0) {
+    return;
+  }
+  // dispose frame with index 'frame_' to offscreen buffer
+  switch (frames[frame].dispose) {
+    case DISPOSE_PREVIOUS: {
+        // dispose to previous restores to first not DISPOSE_TO_PREVIOUS frame
+        int prev(frame);
+        while (prev > 0 && frames[prev].dispose == DISPOSE_PREVIOUS)
+          prev--;
+        if (prev == 0 && frames[prev].dispose == DISPOSE_PREVIOUS) {
+          set_to_background(frame);
+          return;
+        }
+        DEBUG(("  dispose frame %d to previous frame %d\n", frame + 1, prev + 1));
+        // copy the previous image data..
+        uchar *dst = offscreen;
+        int px = frames[prev].x;
+        int py = frames[prev].y;
+        int pw = frames[prev].w;
+        int ph = frames[prev].h;
+        const char *src = frames[prev].rgb->data()[0];
+        if (px == 0 && py == 0 && pw == canvas_w && ph == canvas_h)
+          memcpy((char *)dst, (char *)src, canvas_w * canvas_h * 4);
+        else {
+          if ( px + pw > canvas_w ) pw = canvas_w - px;
+          if ( py + ph > canvas_h ) ph = canvas_h - py;
+          for (int y = 0; y < ph; y++) {
+            memcpy(dst + ( y + py ) * canvas_w * 4 + px, src + y * frames[prev].w * 4, pw * 4);
+          }
+        }
+        break;
+      }
+    case DISPOSE_BACKGROUND:
+      DEBUG(("  dispose frame %d to background\n", frame + 1));
+      set_to_background(frame);
+      break;
+
+    default: {
+        // nothing to do (keep everything as is)
+        break;
+      }
+  }
+}
+
+
+bool Fl_Anim_GIF_Image::FrameInfo::load(const char *name, const unsigned char *data, size_t length) {
+  // decode using FLTK
+  valid = false;
+  if (data) {
+    anim->Fl_GIF_Image::load(name, data, length, true); // calls on_frame_data() for each frame
+  } else {
+    anim->Fl_GIF_Image::load(name, true); // calls on_frame_data() for each frame
+  }
+
+  delete[] offscreen;
+  offscreen = 0;
+  return valid;
+}
+
+
+void Fl_Anim_GIF_Image::FrameInfo::on_extension_data(Fl_GIF_Image::GIF_FRAME &gf) {
+  if (!gf.bptr)
+     return;
+  const uchar *ext = gf.bptr;
+  if (memcmp(ext, "NETSCAPE2.0", 11) == 0) {
+    const uchar *params = &ext[11];
+    loop_count = params[1] | (params[2] << 8);
+    DEBUG(("netscape loop count: %u\n", loop_count));
+  }
+}
+
+
+void Fl_Anim_GIF_Image::FrameInfo::on_frame_data(Fl_GIF_Image::GIF_FRAME &gf) {
+  if (!gf.bptr)
+     return;
+  int delay = gf.delay;
+  if (delay <= 0)
+    delay = -(delay + 1);
+  LOG(("on_frame_data: frame #%d/%d, %dx%d at %d/%d, delay: %d, bkgd=%d/%d, trans=%d, dispose=%d\n",
+        gf.ifrm + 1, -1, gf.w, gf.h, gf.x, gf.y,
+        gf.delay, gf.bkgd, gf.clrs, gf.trans, gf.dispose));
+
+  if (!gf.ifrm) {
+    // first frame, get width/height
+    valid = true; // may be reset later from loading callback
+    canvas_w = gf.width;
+    canvas_h = gf.height;
+    offscreen = new uchar[canvas_w * canvas_h * 4];
+    memset(offscreen, 0, canvas_w * canvas_h * 4);
+  }
+
+  if (!gf.ifrm) {
+    // store background_color AFTER color table is set
+    background_color_index = gf.clrs && gf.bkgd < gf.clrs ? gf.bkgd : -1;
+
+    if (background_color_index >= 0) {
+        background_color = RGBA_Color(gf.cpal[background_color_index].r,
+                                      gf.cpal[background_color_index].g,
+                                      gf.cpal[background_color_index].b);
+    }
+  }
+
+  // process frame
+  frame.x = gf.x;
+  frame.y = gf.y;
+  frame.w = gf.w;
+  frame.h = gf.h;
+  frame.delay = convert_delay(delay);
+  frame.transparent_color_index = gf.trans && gf.trans < gf.clrs ? gf.trans : -1;
+  frame.dispose = (Dispose)gf.dispose;
+  if (frame.transparent_color_index >= 0) {
+    frame.transparent_color = RGBA_Color(gf.cpal[frame.transparent_color_index].r,
+                                         gf.cpal[frame.transparent_color_index].g,
+                                         gf.cpal[frame.transparent_color_index].b);
+  }
+  DEBUG(("#%d %d/%d %dx%d delay: %d, dispose: %d transparent_color: %d\n",
+    (int)frames_size + 1,
+    frame.x, frame.y, frame.w, frame.h,
+    gf.delay, gf.dispose, gf.trans));
+
+  // we know now everything we need about the frame..
+  dispose(frames_size - 1);
+
+  // copy image data to offscreen
+  const uchar *bits = gf.bptr;
+  const uchar *endp = offscreen + canvas_w * canvas_h * 4;
+  for (int y = frame.y; y < frame.y + frame.h; y++) {
+    for (int x = frame.x; x < frame.x + frame.w; x++) {
+      uchar c = *bits++;
+      if (c == gf.trans)
+        continue;
+      uchar *buf = offscreen;
+      buf += (y * canvas_w * 4 + (x * 4));
+      if (buf >= endp)
+        continue;
+      *buf++ = gf.cpal[c].r;
+      *buf++ = gf.cpal[c].g;
+      *buf++ = gf.cpal[c].b;
+      *buf = T_NONE;
+    }
+  }
+
+  // create RGB image from offscreen
+  if (optimize_mem) {
+    uchar *buf = new uchar[frame.w * frame.h * 4];
+    uchar *dest = buf;
+    for (int y = frame.y; y < frame.y + frame.h; y++) {
+      for (int x = frame.x; x < frame.x + frame.w; x++) {
+        if (offscreen + y * canvas_w * 4 + x * 4 < endp)
+          memcpy(dest, &offscreen[y * canvas_w * 4 + x * 4], 4);
+        dest += 4;
+      }
+    }
+    frame.rgb = new Fl_RGB_Image(buf, frame.w, frame.h, 4);
+  }
+  else {
+    uchar *buf = new uchar[canvas_w * canvas_h * 4];
+    memcpy(buf, offscreen, canvas_w * canvas_h * 4);
+    frame.rgb = new Fl_RGB_Image(buf, canvas_w, canvas_h, 4);
+  }
+  frame.rgb->alloc_array = 1;
+
+  if (!push_back_frame(frame)) {
+    valid = false;
+  }
+}
+
+
+bool Fl_Anim_GIF_Image::FrameInfo::push_back_frame(const GifFrame &frame) {
+  void *tmp = realloc(frames, sizeof(GifFrame) * (frames_size + 1));
+  if (!tmp) {
+    return false;
+  }
+  frames = (GifFrame *)tmp;
+  memcpy(&frames[frames_size], &frame, sizeof(GifFrame));
+  frames_size++;
+  return true;
+}
+
+
+void Fl_Anim_GIF_Image::FrameInfo::resize(int W, int H) {
+  double scale_factor_x = (double)W / (double)canvas_w;
+  double scale_factor_y = (double)H / (double)canvas_h;
+  for (int i=0; i < frames_size; i++) {
+    if (optimize_mem) {
+      frames[i].x = lround(frames[i].x * scale_factor_x);
+      frames[i].y = lround(frames[i].y * scale_factor_y);
+      int new_w = (int)lround(frames[i].w * scale_factor_x);
+      int new_h = (int)lround(frames[i].h * scale_factor_y);
+      frames[i].w = new_w;
+      frames[i].h = new_h;
+    }
+  }
+  canvas_w = W;
+  canvas_h = H;
+}
+
+
+void Fl_Anim_GIF_Image::FrameInfo::scale_frame(int frame) {
+  // Do the actual scaling after a resize if neccessary
+  int new_w = optimize_mem ? frames[frame].w : canvas_w;
+  int new_h = optimize_mem ? frames[frame].h : canvas_h;
+  if (frames[frame].scalable &&
+      frames[frame].scalable->w() == new_w &&
+      frames[frame].scalable->h() == new_h)
+    return;
+
+  Fl_RGB_Scaling old_scaling = Fl_Image::RGB_scaling(); // save current scaling method
+  Fl_Image::RGB_scaling(scaling);
+  if (!frames[frame].scalable) {
+    frames[frame].scalable = Fl_Shared_Image::get(frames[frame].rgb, 0);
+  }
+  frames[frame].scalable->scale(new_w, new_h, 0, 1);
+  Fl_Image::RGB_scaling(old_scaling); // restore scaling method
+}
+
+
+void Fl_Anim_GIF_Image::FrameInfo::set_to_background(int frame) {
+  // reset offscreen to background color
+  int bg = background_color_index;
+  int tp = frame >= 0 ? frames[frame].transparent_color_index : bg;
+  DEBUG(("  set_to_background [%d] tp = %d, bg = %d\n", frame, tp, bg));
+  RGBA_Color color = background_color;
+  if (tp >= 0)
+    color = frames[frame].transparent_color;
+  if (tp >= 0 && bg >= 0)
+    bg = tp;
+  color.alpha = tp == bg ? T_FULL : tp < 0 ? T_FULL : T_NONE;
+  DEBUG(("  set to color %d/%d/%d alpha=%d\n", color.r, color.g, color.b, color.alpha));
+  for (uchar *p = offscreen + canvas_w * canvas_h * 4 - 4; p >= offscreen; p -= 4)
+    memcpy(p, &color, 4);
+}
+
+
+void Fl_Anim_GIF_Image::FrameInfo::set_frame(int frame) {
+  // scaling pending?
+  scale_frame(frame);
+
+  // color average pending?
+  if (average_weight >= 0 && average_weight < 1 &&
+      ((average_color != frames[frame].average_color) ||
+       (average_weight != frames[frame].average_weight))) {
+    frames[frame].rgb->color_average(average_color, average_weight);
+    frames[frame].average_color = average_color;
+    frames[frame].average_weight = average_weight;
+  }
+
+  // desaturate pending?
+  if (desaturate && !frames[frame].desaturated) {
+    frames[frame].rgb->desaturate();
+    frames[frame].desaturated = true;
+  }
+}
+
+
+
+///////////////////////////////////////////////////////////////////////
+//
+// Fl_Anim_GIF_Image
+//
+// An extension to Fl_GIF_Image.
+//
+///////////////////////////////////////////////////////////////////////
+
+
+//
+// Fl_Anim_GIF_Image global variables
+//
+
+/*static*/
+double Fl_Anim_GIF_Image::min_delay = 0.;
+/*static*/
+bool Fl_Anim_GIF_Image::loop = true;
+
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <FL/Fl_RGB_Image.H>
+#include <FL/Fl_Group.H>
+#include <FL/Fl.H>
+
+//
+// class Fl_Anim_GIF_Image implementation
+//
+
+/** Load an animated GIF image from a file.
+
+ This constructor creates an animated image form a GIF-formatted file.
+ Optionally it applies the \ref canvas() method after successful load.
+ If \ref DONT_START is not specified in the \p flags parameter it calls
+ \ref start() after successful load.
+
+ \param[in] filename  path and name of GIF file in the file system
+ \param[in] canvas    a widget that will show and animate the GIF, or \c NULL
+ \param[in] flags     see \ref Flags for details, or 0
+
+ \note The GIF image must be decoupled from the canvas by calling
+    `myGif->canvas(NULL);` before deleting the canvas.
+ */
+Fl_Anim_GIF_Image::Fl_Anim_GIF_Image(const char *filename,
+                                     Fl_Widget *canvas /* = 0*/,
+                                     unsigned short flags /* = 0 */) :
+  Fl_GIF_Image(),
+  name_(0),
+  flags_(flags),
+  canvas_(canvas),
+  uncache_(false),
+  valid_(false),
+  frame_(-1),
+  speed_(1.),
+  fi_(new FrameInfo(this))
+{
+  fi_->debug_ = ((flags_ & LOG_FLAG) != 0) + 2 * ((flags_ & DEBUG_FLAG) != 0);
+  fi_->optimize_mem = (flags_ & OPTIMIZE_MEMORY);
+  valid_ = load(filename, NULL, 0);
+  if (canvas_w() && canvas_h()) {
+    if (!w() && !h()) {
+      w(canvas_w());
+      h(canvas_h());
+    }
+  }
+  this->canvas(canvas, flags);
+  if (!(flags & DONT_START))
+    start();
+  else
+    frame_ = 0;
+}
+
+
+/** Load an animated GIF image from memory.
+
+ This constructor creates an animated image form a GIF-formatted block in
+ memory. Optionally it applies the \ref canvas() method after successful load.
+ If \ref DONT_START is not specified in the \p flags parameter it calls
+ \ref start() after successful load.
+
+ \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.
+
+ \param[in] imagename   a name given to this image or \c NULL
+ \param[in] data        pointer to the start of the GIF image in memory
+ \param[in] length      length of the GIF image in memory
+ \param[in] canvas      a widget that will show and animate the GIF, or \c NULL
+ \param[in] flags       see \ref Flags for details, or 0
+
+ \note The GIF image must be decoupled from the canvas by calling
+    `myGif->canvas(NULL);` before deleting the canvas.
+ */
+Fl_Anim_GIF_Image::Fl_Anim_GIF_Image(const char* imagename, const unsigned char *data,
+                                     const size_t length, Fl_Widget *canvas /* = 0 */,
+                                     unsigned short flags /* = 0 */) :
+  Fl_GIF_Image(),
+  name_(0),
+  flags_(flags),
+  canvas_(canvas),
+  uncache_(false),
+  valid_(false),
+  frame_(-1),
+  speed_(1.),
+  fi_(new FrameInfo(this))
+{
+  fi_->debug_ = ((flags_ & LOG_FLAG) != 0) + 2 * ((flags_ & DEBUG_FLAG) != 0);
+  fi_->optimize_mem = (flags_ & OPTIMIZE_MEMORY);
+  valid_ = load(imagename, data, length);
+  if (canvas_w() && canvas_h()) {
+    if (!w() && !h()) {
+      w(canvas_w());
+      h(canvas_h());
+    }
+  }
+  this->canvas(canvas, flags);
+  if (!(flags & DONT_START))
+    start();
+  else
+    frame_ = 0;
+}
+
+
+/** Create an empty animated GIF image shell. */
+Fl_Anim_GIF_Image::Fl_Anim_GIF_Image() :
+  Fl_GIF_Image(),
+  name_(0),
+  flags_(0),
+  canvas_(0),
+  uncache_(false),
+  valid_(false),
+  frame_(-1),
+  speed_(1.),
+  fi_(new FrameInfo(this)) {
+}
+
+
+/** Release the image and all cached data.
+
+ Also removes the animation timer.
+ */
+Fl_Anim_GIF_Image::~Fl_Anim_GIF_Image() /* override */ {
+  Fl::remove_timeout(cb_animate, this);
+  delete fi_;
+  free(name_);
+}
+
+
+/** Link the image back to a widget for automated animation.
+
+ This method sets current widget, that is used to display the frame images.
+ The \p flags parameter specifies whether the canvas widget is resized to the
+ animation dimensions and/or its \ref image() method will be used to set the
+ current frame image during animation.
+
+ \param[in] canvas a pointer to the widget that will show the animation
+ \param[in] flags see \ref Flags
+
+ \note The GIF image must be decoupled from the canvas by calling
+    `myGif->canvas(NULL);` before deleting the canvas.
+ */
+void Fl_Anim_GIF_Image::canvas(Fl_Widget *canvas, unsigned short flags/* = 0*/) {
+  if (canvas_)
+    canvas_->image(0);
+  canvas_ = canvas;
+  if (canvas_ && !(flags & DONT_SET_AS_IMAGE))
+    canvas_->image(this); // set animation as image() of canvas
+  if (canvas_ && !(flags & DONT_RESIZE_CANVAS))
+    canvas_->size(w(), h());
+  if (flags_ != flags) {
+    flags_ = flags;
+    fi_->debug_ = ((flags & LOG_FLAG) != 0) + 2 * ((flags & DEBUG_FLAG) != 0);
+  }
+  // Note: 'Start' flag is *NOT* used here,
+  //       but an already running animation is restarted.
+  frame_ = -1;
+  if (Fl::has_timeout(cb_animate, this)) {
+    Fl::remove_timeout(cb_animate, this);
+    next_frame();
+  }
+  else if ( fi_->frames_size ) {
+    set_frame(0);
+  }
+}
+
+
+/** Gets the current widget, that is used to display the frame images.
+ \return a pointer to a widget
+ */
+Fl_Widget *Fl_Anim_GIF_Image::canvas() const {
+  return canvas_;
+}
+
+
+/** Return the width of the animation canvas.
+ \return the width in pixel units
+ */
+int Fl_Anim_GIF_Image::canvas_w() const {
+  return fi_->canvas_w;
+}
+
+
+/** Return the height of the animation canvas.
+ \return the width in pixel units
+ */
+int Fl_Anim_GIF_Image::canvas_h() const {
+  return fi_->canvas_h;
+}
+
+
+/*static*/
+void Fl_Anim_GIF_Image::cb_animate(void *d) {
+  Fl_Anim_GIF_Image *b = (Fl_Anim_GIF_Image *)d;
+  b->next_frame();
+}
+
+
+void Fl_Anim_GIF_Image::clear_frames() {
+  fi_->clear();
+  valid_ = false;
+}
+
+
+/** Applies a color average to all frames.
+
+ The color_average() method averages the colors in the image with
+ the provided FLTK color value.
+
+ \param[in] c blend color
+ \param[in] i a value between 0.0 and 1.0 where 0 results in the blend color,
+      and 1 returns the original image
+ */
+void Fl_Anim_GIF_Image::color_average(Fl_Color c, float i) /* override */ {
+  if (i < 0) {
+    // immediate mode
+    i = -i;
+    for (int f=0; f < frames(); f++) {
+      fi_->frames[f].rgb->color_average(c, i);
+    }
+    return;
+  }
+  fi_->average_color = c;
+  fi_->average_weight = i;
+  //  Do not call set_frame()! If this is called with an indexed color before
+  //  the display connection is open, it will possible average *this* frame
+  //  with a different RGB value than the following frames.
+  //  See Fl_Image::inactive().
+  // set_frame();
+}
+
+
+/** Copy and resize the animation frames.
+
+ The virtual copy() method makes a copy of the animated image and resizes all
+ of its frame images to W x H using the current resize method.
+
+ \param[in] W, H new size in FLTK pixel units
+ \return the resized copy of the animation
+ */
+Fl_Image *Fl_Anim_GIF_Image::copy(int W, int H) const /* override */ {
+  Fl_Anim_GIF_Image *copied = new Fl_Anim_GIF_Image();
+  // copy/resize the base image (Fl_Pixmap)
+  // Note: this is not really necessary, if the draw()
+  //       method never calls the base class.
+  if (fi_->frames_size) {
+    Fl_Pixmap *gif = (Fl_Pixmap *)Fl_GIF_Image::copy(W, H);
+    copied->Fl_GIF_Image::data(gif->data(), gif->count());
+    copied->alloc_data = gif->alloc_data;
+    gif->alloc_data = 0;
+    delete gif;
+  }
+
+  if (name_) copied->name_ = strdup(name_);
+  copied->flags_ = flags_;
+  copied->frame_ = frame_;
+  copied->speed_ = speed_;
+
+  copied->w(W);
+  copied->h(H);
+  copied->fi_->canvas_w = W;
+  copied->fi_->canvas_h = H;
+  copied->fi_->copy(*fi_); // copy the meta data
+
+  copied->uncache_ = uncache_; // copy 'inherits' frame uncache status
+  copied->valid_ = valid_ && copied->fi_->frames_size == fi_->frames_size;
+  copied->scale_frame(); // scale current frame now
+  if (copied->valid_ && frame_ >= 0 && !Fl::has_timeout(cb_animate, copied))
+    copied->start(); // start if original also was started
+  return copied;
+}
+
+
+int Fl_Anim_GIF_Image::debug() const {
+  return fi_->debug();
+}
+
+
+/** Return the delay of frame `[0-frames() -1]` in seconds.
+
+ \param[in] frame index into frame list
+ \return delay to next frame in seconds
+ */
+double Fl_Anim_GIF_Image::delay(int frame) const {
+  if (frame >= 0 && frame < frames())
+    return fi_->frames[frame].delay;
+  return 0.;
+}
+
+
+/** Set the delay of frame `[0-frames() -1]` in seconds.
+
+ \param[in] frame index into frame list
+ \param[in] delay to next frame in seconds
+ */
+void Fl_Anim_GIF_Image::delay(int frame, double delay) {
+  if (frame >= 0 && frame < frames())
+    fi_->frames[frame].delay = delay;
+}
+
+
+/** Desaturate to all frames of the animation.
+ */
+void Fl_Anim_GIF_Image::desaturate() /* override */ {
+  fi_->desaturate = true;
+  set_frame();
+}
+
+
+/** Draw the current frame of the animation.
+
+ \param[in] x, y, w, h target rectangle
+ \param[in] cx, cy source offset
+ */
+void Fl_Anim_GIF_Image::draw(int x, int y, int w, int h,
+                             int cx/* = 0*/, int cy/* = 0*/) /* override */ {
+  if (this->image()) {
+    if (fi_->optimize_mem) {
+      int f0 = frame_;
+      while (f0 > 0 && !(fi_->frames[f0].x == 0 && fi_->frames[f0].y == 0 &&
+                       fi_->frames[f0].w == this->w() && fi_->frames[f0].h == this->h()))
+        --f0;
+      for (int f = f0; f <= frame_; f++) {
+        if (f < frame_ && fi_->frames[f].dispose == FrameInfo::DISPOSE_PREVIOUS) continue;
+        if (f < frame_ && fi_->frames[f].dispose == FrameInfo::DISPOSE_BACKGROUND) continue;
+        Fl_RGB_Image *rgb = fi_->frames[f].rgb;
+        if (rgb) {
+          float s = Fl_Graphics_Driver::default_driver().scale();
+	        rgb->scale(s*fi_->frames[f].w, s*fi_->frames[f].h, 0, 1);
+          rgb->draw(x + s*fi_->frames[f].x, y + s*fi_->frames[f].y, w, h, cx, cy);
+        }
+      }
+    }
+    else {
+      this->image()->scale(Fl_GIF_Image::w(), Fl_GIF_Image::h(), 0, 1);
+      this->image()->draw(x, y, w, h, cx, cy);
+    }
+  } else {
+    // Note: should the base class be called here?
+    //       If it is, then the copy() method must also
+    //       copy the base image!
+    Fl_GIF_Image::draw(x, y, w, h, cx, cy);
+  }
+}
+
+
+/** Return the current frame.
+
+ \return the current frame index in the range for 0 to `frames()-1`.
+ \return -1 if the image has no frames.
+ */
+int Fl_Anim_GIF_Image::frame() const {
+  return frame_;
+}
+
+
+/** Set the current frame.
+
+ \param[in] frame index into list of frames
+ */
+void Fl_Anim_GIF_Image::frame(int frame) {
+  if (Fl::has_timeout(cb_animate, this)) {
+    Fl::warning("Fl_Anim_GIF_Image::frame(%d): not idle!\n", frame);
+    return;
+  }
+  if (frame >= 0 && frame < frames()) {
+    set_frame(frame);
+  }
+  else {
+    Fl::warning("Fl_Anim_GIF_Image::frame(%d): out of range!\n", frame);
+  }
+}
+
+
+/** Get the number of frames in a GIF file or in a GIF compressed data block.
+
+ The static frame_count() method is just a convenience method for
+ getting the number of images (frames) stored in a GIF file.
+
+ As this count is not readily available in the GIF header, the
+ whole GIF file has be parsed (which is done here by using a
+ temporary Fl_Anim_GIF_Image object for simplicity).
+ So this call may be slow with large files.
+
+ If \p imgdata is \c NULL, the image will be read from the file. Otherwise, it will
+ be read from memory.
+
+ \param[in] name      path and name of GIF file in the file system, ignored
+                      when reading from memeory
+ \param[in] imgdata   pointer to the start of the GIF image in memory, or
+                      \c NULL to read from a file
+ \param[in] imglength length of the GIF image in memory, or \c 0
+
+ \return the number of frames in the animation
+ */
+int Fl_Anim_GIF_Image::frame_count(const char *name, const unsigned char *imgdata /* = NULL */, size_t imglength /* = 0 */) {
+  Fl_Anim_GIF_Image temp;
+  temp.load(name, imgdata, imglength);
+  int frames = temp.valid() ? temp.frames() : 0;
+  return frames;
+}
+
+
+/** Return the frame position of a frame.
+
+ Usefull only if loaded with 'optimize_mem' and
+ the animation also has size optimized frames.
+
+ \param[in] frame index into frame list
+ \return x position in FLTK pixle units
+ */
+int Fl_Anim_GIF_Image::frame_x(int frame) const {
+  if (frame >= 0 && frame < frames())
+    return fi_->frames[frame].x;
+  return -1;
+}
+
+
+/** Return the frame position of a frame.
+
+ Usefull only if loaded with 'optimize_mem' and
+ the animation also has size optimized frames.
+
+ \param[in] frame index into frame list
+ \return y position in FLTK pixle units
+ */
+int Fl_Anim_GIF_Image::frame_y(int frame) const {
+  if (frame >= 0 && frame < frames())
+    return fi_->frames[frame].y;
+  return -1;
+}
+
+
+/** Return the frame dimensions of a frame.
+
+ Usefull only if loaded with 'optimize_mem' and
+ the animation also has size optimized frames.
+
+ \param[in] frame index into frame list
+ \return width in FLTK pixle units
+ */
+int Fl_Anim_GIF_Image::frame_w(int frame) const {
+  if (frame >= 0 && frame < frames())
+    return fi_->frames[frame].w;
+  return -1;
+}
+
+
+/** Return the frame dimensions of a frame.
+
+ Usefull only if loaded with 'optimize_mem' and
+ the animation also has size optimized frames.
+
+ \param[in] frame index into frame list
+ \return height in FLTK pixle units
+ */
+int Fl_Anim_GIF_Image::frame_h(int frame) const {
+  if (frame >= 0 && frame < frames())
+    return fi_->frames[frame].h;
+  return -1;
+}
+
+
+/** Use frame_uncache() to set or forbid frame image uncaching.
+
+ If frame uncaching is set, frame images are not offscreen cached
+ for re-use and will be re-created every time they are displayed.
+ This saves a lot of memory on the expense of cpu usage and
+ should be carefully considered. Per default frame caching will
+ be done.
+
+ \param[in] uncache true to disable caching
+ */
+void Fl_Anim_GIF_Image::frame_uncache(bool uncache) {
+  uncache_ = uncache;
+}
+
+
+/** Return the active frame_uncache() setting.
+ \return true if caching is disabled
+ */
+bool Fl_Anim_GIF_Image::frame_uncache() const {
+  return uncache_;
+}
+
+
+/** Get the number of frames in the animation.
+ \return the number of frames
+ */
+int Fl_Anim_GIF_Image::frames() const {
+  return fi_->frames_size;
+}
+
+
+/** Return the current frame image.
+ \return a pointer to the image or NULL if this is not an animation.
+ */
+Fl_Image *Fl_Anim_GIF_Image::image() const {
+  return frame_ >= 0 && frame_ < frames() ? fi_->frames[frame_].rgb : 0;
+}
+
+
+/** Return the image of the given frame index.
+
+ \param[in] frame_ index into list of frames
+ \return image data or NULL if the frame number is not valid.
+ */
+Fl_Image *Fl_Anim_GIF_Image::image(int frame_) const {
+  if (frame_ >= 0 && frame_ < frames())
+    return fi_->frames[frame_].rgb;
+  return 0;
+}
+
+
+/** Check if this is a valid animation with more than one frame.
+
+ The \c is_animated() method is just a convenience method for testing the valid
+ flag and the frame count beeing greater 1.
+
+ \return true if the animation is valid and has multiple frames.
+ */
+bool Fl_Anim_GIF_Image::is_animated() const {
+  return valid_ && fi_->frames_size > 1;
+}
+
+
+/*static*/
+bool Fl_GIF_Image::is_animated(const char *name) {
+  return Fl_Anim_GIF_Image::frame_count(name) > 1;
+}
+
+
+/** Load an animation from a file or from a memory block.
+
+ The load() method is either used from the constructor to load the image from
+ the given file, or to re-load an existing animation from another file.
+
+ \param[in] name      path and name of GIF file in the file system, or the image
+                      name when reading from memory
+ \param[in] imgdata   pointer to the start of the GIF image in memory, or
+                      \c NULL to read from a file
+ \param[in] imglength length of the GIF image in memory, or \c 0
+ \return true if the animation loaded correctly
+ */
+bool Fl_Anim_GIF_Image::load(const char *name, const unsigned char *imgdata /* =NULL */, size_t imglength /* =0 */) {
+  DEBUG(("\nFl_Anim_GIF_Image::load '%s'\n", name));
+  clear_frames();
+  free(name_);
+  name_ = name ? strdup(name) : 0;
+
+  // as load() can be called multiple times
+  // we have to replicate the actions of the pixmap destructor here
+  uncache();
+  if (alloc_data) {
+    for (int i = 0; i < count(); i ++) delete[] (char *)data()[i];
+    delete[] (char **)data();
+  }
+  alloc_data = 0;
+  w(0);
+  h(0);
+
+  if (name_) {
+    fi_->load(name, imgdata, imglength);
+  }
+
+  frame_ = fi_->frames_size - 1;
+  valid_ = fi_->valid;
+
+  if (!valid_) {
+    Fl::error("Fl_Anim_GIF_Image: %s has invalid format.\n", name_);
+    ld(ERR_FORMAT);
+  }
+  return valid_;
+} // load
+
+
+/** Return the name of the played file as specified in the constructor.
+
+ If read from a memory block, this returns the name of the animation.
+
+ \return pointer to a C string
+ */
+const char *Fl_Anim_GIF_Image::name() const {
+  return name_;
+}
+
+
+bool Fl_Anim_GIF_Image::next_frame() {
+  int frame(frame_);
+  frame++;
+  if (frame >= fi_->frames_size)  {
+    fi_->loop++;
+    if (Fl_Anim_GIF_Image::loop && fi_->loop_count > 0 && fi_->loop > fi_->loop_count) {
+      DEBUG(("loop count %d reached - stopped!\n", fi_->loop_count));
+      stop();
+    }
+    else
+      frame = 0;
+  }
+  if (frame >= fi_->frames_size)
+    return false;
+  set_frame(frame);
+  double delay = fi_->frames[frame].delay;
+  if (min_delay && delay < min_delay) {
+    DEBUG(("#%d: correct delay %f => %f\n", frame, delay, min_delay));
+    delay = min_delay;
+  }
+  if (is_animated() && delay > 0 && speed_ > 0) {  // normal GIF has no delay
+    delay /= speed_;
+    Fl::add_timeout(delay, cb_animate, this);
+  }
+  return true;
+}
+
+
+/*virtual*/
+void Fl_Anim_GIF_Image::on_frame_data(Fl_GIF_Image::GIF_FRAME &gf) {
+  fi_->on_frame_data(gf);
+}
+
+
+/*virtual*/
+void Fl_Anim_GIF_Image::on_extension_data(Fl_GIF_Image::GIF_FRAME &gf) {
+  fi_->on_extension_data(gf);
+}
+
+
+/** Resizes the image to the specified size, replacing the current image.
+
+ If \ref DONT_RESIZE_CANVAS is not set, the canvas widget will also be resized.
+
+ \param[in] w, h new size of teh naimtion frames
+ */
+Fl_Anim_GIF_Image& Fl_Anim_GIF_Image::resize(int w, int h) {
+  int W(w);
+  int H(h);
+  if (canvas_ && !W && !H) {
+    W = canvas_->w();
+    H = canvas_->h();
+  }
+  if (!W || !H || ((W == this->w() && H == this->h()))) {
+    return *this;
+  }
+  fi_->resize(W, H);
+  scale_frame(); // scale current frame now
+  this->w(fi_->canvas_w);
+  this->h(fi_->canvas_h);
+  if (canvas_ && !(flags_ & DONT_RESIZE_CANVAS)) {
+    canvas_->size(this->w(), this->h());
+  }
+  return *this;
+}
+
+
+/** Resizes the image to the specified size, replacing the current image.
+
+ If \ref DONT_RESIZE_CANVAS is not set, the canvas widget will also be resized.
+
+ \param[in] scale rescale factor in relation to current size
+ */
+Fl_Anim_GIF_Image& Fl_Anim_GIF_Image::resize(double scale) {
+  return resize((int)lround((double)w() * scale), (int)lround((double)h() * scale));
+}
+
+
+void Fl_Anim_GIF_Image::scale_frame() {
+  int i(frame_);
+  if (i < 0)
+    return;
+  fi_->scale_frame(i);
+}
+
+
+void Fl_Anim_GIF_Image::set_frame() {
+  int i(frame_);
+  if (i < 0)
+    return;
+  fi_->set_frame(i);
+}
+
+
+void Fl_Anim_GIF_Image::set_frame(int frame) {
+//  int last_frame = frame_;
+  frame_ = frame;
+  // NOTE: uncaching decreases performance, but saves a lot of memory
+  if (uncache_ && this->image())
+    this->image()->uncache();
+
+  fi_->set_frame(frame_);
+
+  Fl_Widget* cv = canvas();
+  if (cv) {
+    Fl_Group* parent = cv->parent();
+    bool no_bg = (cv->box() == FL_NO_BOX);
+    bool outside = (!(cv->align() & FL_ALIGN_INSIDE) && !((cv->align() & FL_ALIGN_POSITION_MASK)==FL_ALIGN_CENTER));
+//    bool dispose =    (fi_->frames[last_frame].dispose == FrameInfo::DISPOSE_BACKGROUND)
+//                   || (fi_->frames[last_frame].dispose == FrameInfo::DISPOSE_PREVIOUS);
+    if (parent && (no_bg || outside))
+      parent->redraw();
+    else
+      cv->redraw();
+
+// Note: the code below did not animate labels with a pixmap outside of the canvas
+//    canvas()->parent() &&
+//      (frame_ == 0 || (last_frame >= 0 && (fi_->frames[last_frame].dispose == FrameInfo::DISPOSE_BACKGROUND  ||
+//                                           fi_->frames[last_frame].dispose == FrameInfo::DISPOSE_PREVIOUS))) &&
+//        (canvas()->box() == FL_NO_BOX || (canvas()->align() && !(canvas()->align() & FL_ALIGN_INSIDE)))      ?
+//      canvas()->parent()->redraw() : canvas()->redraw();
+
+  }
+}
+
+
+/** Get the animation speed factor.
+ \return the current speed factor
+ */
+double Fl_Anim_GIF_Image::speed() const {
+  return speed_;
+}
+
+
+/** Set the animation speed factor.
+
+ The speed() method changes the playing speed to \p speed x original speed.
+ E.g. to play at half speed call it with 0.5, for double speed with 2.
+
+ \param[in] speed floating point speed factor
+ */
+void Fl_Anim_GIF_Image::speed(double speed) {
+  speed_ = speed;
+}
+
+
+/** The start() method (re-)starts the playing of the frames.
+ \return true if the animation has frames
+ */
+bool Fl_Anim_GIF_Image::start() {
+  Fl::remove_timeout(cb_animate, this);
+  if (fi_->frames_size) {
+    next_frame();
+  }
+  return fi_->frames_size != 0;
+}
+
+
+/** The stop() method stops the playing of the frames.
+ \return true if the animation has frames
+ */
+bool Fl_Anim_GIF_Image::stop() {
+  Fl::remove_timeout(cb_animate, this);
+  return fi_->frames_size != 0;
+}
+
+
+/** Show the next frame if the animation is stopped.
+ \return true if the animation has frames
+ */
+bool Fl_Anim_GIF_Image::next() {
+  if (fi_->frames_size && !(Fl::has_timeout(cb_animate, this))) {
+    int f = frame() + 1;
+    if (f >= frames()) f = 0;
+    frame(f);
+  }
+  return fi_->frames_size != 0;
+}
+
+
+/** Uncache all cached image data now.
+ Re-implemented from Fl_Pixmap.
+ */
+void Fl_Anim_GIF_Image::uncache() /* override */ {
+  Fl_GIF_Image::uncache();
+  for (int i=0; i < fi_->frames_size; i++) {
+    if (fi_->frames[i].rgb) fi_->frames[i].rgb->uncache();
+  }
+}
+
+
+/** Check if animation is valid.
+ \return true if the class has successfully loaded and the image has at least
+         one frame.
+ */
+bool Fl_Anim_GIF_Image::valid() const {
+  return valid_;
+}
diff --git src/Fl_GIF_Image.cxx src/Fl_GIF_Image.cxx
index a8b6250..93fe22f 100644
--- src/Fl_GIF_Image.cxx
+++ 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);
+  }
+}
diff --git src/Makefile src/Makefile
index cbba407..f491ce2 100644
--- src/Makefile
+++ src/Makefile
@@ -230,6 +230,7 @@ IMGCPPFILES = \
 	Fl_BMP_Image.cxx \
 	Fl_File_Icon2.cxx \
 	Fl_GIF_Image.cxx \
+	Fl_Anim_GIF_Image.cxx \
 	Fl_Help_Dialog.cxx \
 	Fl_ICO_Image.cxx \
 	Fl_JPEG_Image.cxx \
diff --git src/fl_images_core.cxx src/fl_images_core.cxx
index 5c2a7e9..431e7c9 100644
--- src/fl_images_core.cxx
+++ src/fl_images_core.cxx
@@ -27,6 +27,7 @@
 #include <FL/Fl_Shared_Image.H>
 #include <FL/Fl_BMP_Image.H>
 #include <FL/Fl_GIF_Image.H>
+#include <FL/Fl_Anim_GIF_Image.H>
 #include <FL/Fl_JPEG_Image.H>
 #include <FL/Fl_PNG_Image.H>
 #include <FL/Fl_PNM_Image.H>
@@ -89,7 +90,8 @@ fl_check_images(const char *name,               // I - Filename
 
   if (memcmp(header, "GIF87a", 6) == 0 ||
       memcmp(header, "GIF89a", 6) == 0) // GIF file
-    return new Fl_GIF_Image(name);
+    return Fl_GIF_Image::animate ? new Fl_Anim_GIF_Image(name) :
+                                   new Fl_GIF_Image(name);
 
   // BMP
 
diff --git src/makedepend src/makedepend
index 5db28d5..108b024 100644
--- src/makedepend
+++ src/makedepend
@@ -1954,6 +1954,11 @@ Fl_get_system_colors.o: flstring.h
 Fl_get_system_colors.o: Fl_Screen_Driver.H
 Fl_get_system_colors.o: Fl_System_Driver.H
 Fl_get_system_colors.o: tile.xpm
+Fl_Anim_GIF_Image.o: ../config.h
+Fl_Anim_GIF_Image.o: ../FL/Fl_Anim_GIF_Image.H
+Fl_Anim_GIF_Image.o: ../FL/Fl_GIF_Image.H
+Fl_Anim_GIF_Image.o: ../FL/Fl_Image.H
+Fl_Anim_GIF_Image.o: ../FL/Fl_Pixmap.H
 Fl_GIF_Image.o: ../config.h
 Fl_GIF_Image.o: ../FL/Enumerations.H
 Fl_GIF_Image.o: ../FL/Fl.H
diff --git test/CMakeLists.txt test/CMakeLists.txt
index fc8e26e..67c36fb 100644
--- test/CMakeLists.txt
+++ test/CMakeLists.txt
@@ -113,7 +113,7 @@ CREATE_EXAMPLE (navigation navigation.cxx fltk)
 CREATE_EXAMPLE (output output.cxx fltk)
 CREATE_EXAMPLE (overlay overlay.cxx fltk)
 CREATE_EXAMPLE (pack pack.cxx fltk)
-CREATE_EXAMPLE (pixmap pixmap.cxx fltk)
+CREATE_EXAMPLE (pixmap pixmap.cxx "fltk_images;fltk")
 CREATE_EXAMPLE (pixmap_browser pixmap_browser.cxx "fltk_images;fltk")
 CREATE_EXAMPLE (preferences preferences.fl fltk)
 CREATE_EXAMPLE (offscreen offscreen.cxx fltk)
diff --git test/Makefile test/Makefile
index df0aee0..193cd9d 100644
--- test/Makefile
+++ test/Makefile
@@ -512,7 +512,10 @@ overlay$(EXEEXT): overlay.o
 
 pack$(EXEEXT): pack.o
 
-pixmap$(EXEEXT): pixmap.o
+pixmap$(EXEEXT): pixmap.o $(IMGLIBNAME)
+	echo Linking $@...
+	$(CXX) $(ARCHFLAGS) $(CXXFLAGS) $(LDFLAGS) -o $@ pixmap.o $(LINKFLTKIMG) $(LDLIBS)
+	$(OSX_ONLY) ../fltk-config --post $@
 
 pixmap_browser$(EXEEXT): pixmap_browser.o $(IMGLIBNAME)
 	echo Linking $@...
diff --git test/help_dialog.cxx test/help_dialog.cxx
index 5970792..e87c5e9 100644
--- test/help_dialog.cxx
+++ test/help_dialog.cxx
@@ -24,9 +24,18 @@
 //
 
 #include <FL/Fl_Help_Dialog.H>
+#include <FL/Fl_GIF_Image.H>
 #include <FL/filename.H>        /* FL_PATH_MAX */
+#include <FL/Fl.H>              /* Fl::first_window(), etc */
 #include <string.h>             /* strcpy(), etc */
 
+static void cb_refresh(void *d) {
+  // trigger a redraw of the window to see animated GIF's
+  if (Fl::first_window())
+    Fl::first_window()->redraw();
+  Fl::repeat_timeout(1./10, cb_refresh, d);
+}
+
 //
 // 'main()' - Display the help GUI...
 //
@@ -35,6 +44,7 @@ int                             // O - Exit status
 main(int  argc,                 // I - Number of command-line arguments
      char *argv[])              // I - Command-line arguments
 {
+  Fl_GIF_Image::animate = true; // create animated shared .GIF images
   Fl_Help_Dialog *help = new Fl_Help_Dialog;
   int i;
   if (!Fl::args(argc, argv, i)) Fl::fatal(Fl::help);
@@ -44,6 +54,7 @@ main(int  argc,                 // I - Number of command-line arguments
 
   help->show(1, argv);
 
+  Fl::add_timeout(1./10, cb_refresh, help); // to animate GIF's
   Fl::run();
 
   delete help;
diff --git test/help_dialog.html test/help_dialog.html
index f7d7fc0..06492e6 100644
--- test/help_dialog.html
+++ test/help_dialog.html
@@ -264,6 +264,9 @@ Nested OL/UL:
     <TD><IMG SRC="images/tiny.png" alt="Tiny FLTK logo"></TD>
     <TD>Tiny FLTK logo.</TD>
   </TR><TR>
+    <TD><IMG SRC="pixmaps/fltk_animated.gif" WIDTH="200" HEIGHT="50" alt="Animated FLTK logo"></TD>
+    <TD>Animated FLTK logo.</TD>
+  </TR><TR>
     <TD><IMG SRC="images/Fl_Value_Input.png" alt="Fl_Value_Input"></TD>
     <TD>This is an image of Fl_Value_Input</TD>
   </TR><TR>
diff --git test/pixmap.cxx test/pixmap.cxx
index c8fb544..bc08164 100644
--- test/pixmap.cxx
+++ test/pixmap.cxx
@@ -18,17 +18,20 @@
 #include <FL/Fl_Double_Window.H>
 #include <FL/Fl_Button.H>
 #include <FL/Fl_Pixmap.H>
+#include <FL/Fl_Anim_GIF_Image.H>
 #include <stdio.h>
 
-#include "pixmaps/porsche.xpm"
+#include "pixmaps/animated_fluid_gif.h"
 
 #include <FL/Fl_Toggle_Button.H>
 
 Fl_Toggle_Button *leftb,*rightb,*topb,*bottomb,*insideb,*overb,*inactb;
 Fl_Button *b;
 Fl_Double_Window *w;
+Fl_Anim_GIF_Image *pixmap;
+Fl_Anim_GIF_Image *depixmap;
 
-void button_cb(Fl_Widget *,void *) {
+void button_cb(Fl_Widget *wgt,void *) {
   int i = 0;
   if (leftb->value()) i |= FL_ALIGN_LEFT;
   if (rightb->value()) i |= FL_ALIGN_RIGHT;
@@ -42,6 +45,21 @@ void button_cb(Fl_Widget *,void *) {
   w->redraw();
 }
 
+void play_cb(Fl_Widget *wgt,void *) {
+  pixmap->start();
+  depixmap->start();
+}
+
+void stop_cb(Fl_Widget *wgt,void *) {
+  pixmap->stop();
+  depixmap->stop();
+}
+
+void step_cb(Fl_Widget *wgt,void *) {
+  pixmap->next();
+  depixmap->next();
+}
+
 int dvisual = 0;
 int arg(int, char **argv, int &i) {
   if (argv[i][1] == '8') {dvisual = 1; i++; return 1;}
@@ -53,18 +71,24 @@ int arg(int, char **argv, int &i) {
 int main(int argc, char **argv) {
   int i = 1;
   if (Fl::args(argc,argv,i,arg) < argc)
-    Fl::fatal(" -8 # : use default visual\n%s\n",Fl::help);
+    Fl::fatal(" -8 # : use default visual\n%s\n", Fl::help);
+  if (!dvisual) Fl::visual(FL_RGB);
 
-  Fl_Double_Window window(400,400); ::w = &window;
-  Fl_Button b(140,160,120,120,"Pixmap"); ::b = &b;
-  Fl_Pixmap *pixmap = new Fl_Pixmap(porsche_xpm);
-  Fl_Pixmap *depixmap;
-  depixmap = (Fl_Pixmap *)pixmap->copy();
+  Fl_Double_Window window(400,440); ::w = &window;
+  Fl_Button b(130,170,140,140,"Pixmap"); ::b = &b;
+
+  Fl_Anim_GIF_Image::animate = true;
+  pixmap = new Fl_Anim_GIF_Image("fluid", animated_fluid_gif, animated_fluid_gif_size,
+                                 &b, Fl_Anim_GIF_Image::DONT_RESIZE_CANVAS);
+  pixmap->speed(0.5);
+  b.image(pixmap);
+
+  depixmap = (Fl_Anim_GIF_Image*)pixmap->copy();
   depixmap->inactive();
+  b.deimage(depixmap);
 
   // "bind" images to avoid memory leak reports (valgrind, asan)
   // note: these reports are benign because they appear at exit, but anyway
-
   b.bind_image(pixmap);
   b.bind_deimage(depixmap);
 
@@ -82,7 +106,17 @@ int main(int argc, char **argv) {
   overb->callback(button_cb);
   inactb = new Fl_Toggle_Button(125,75,100,25,"inactive");
   inactb->callback(button_cb);
-  if (!dvisual) Fl::visual(FL_RGB);
+
+  Fl_Button* play = new Fl_Button(300, 50, 25, 25, "@>");
+  play->labelcolor(FL_DARK2);
+  play->callback(play_cb);
+  Fl_Button* stop = new Fl_Button(325, 50, 25, 25, "@||");
+  stop->labelcolor(FL_DARK2);
+  stop->callback(stop_cb);
+  Fl_Button* step = new Fl_Button(350, 50, 25, 25, "@|>");
+  step->labelcolor(FL_DARK2);
+  step->callback(step_cb);
+
   window.resizable(window);
   window.end();
   window.show(argc,argv);
diff --git test/pixmap_browser.cxx test/pixmap_browser.cxx
index e9fc1cc..dca1331 100644
--- test/pixmap_browser.cxx
+++ test/pixmap_browser.cxx
@@ -20,6 +20,7 @@
 #include <FL/Fl_Double_Window.H>
 #include <FL/Fl_Button.H>
 #include <FL/Fl_Shared_Image.H>
+#include <FL/Fl_GIF_Image.H>
 #include <FL/Fl_Printer.H>
 #include <string.h>
 #include <errno.h>
@@ -36,6 +37,17 @@ Fl_Shared_Image *img;
 
 static char name[1024];
 
+void cb_forced_redraw(void *) {
+  Fl_Window *win = Fl::first_window();
+  while (win) {
+    if (!win->menu_window())
+      win->redraw();
+    win = Fl::next_window(win);
+  }
+  if (Fl::first_window())
+    Fl::repeat_timeout(1./10, cb_forced_redraw);
+}
+
 void load_file(const char *n) {
   if (img) {
     ((Fl_Shared_Image*)b->image())->release();
@@ -118,8 +130,10 @@ void svg_cb(Fl_Widget *widget, void *) {
 }
 
 int dvisual = 0;
+int animate = 1;
 int arg(int, char **argv, int &i) {
   if (argv[i][1] == '8') {dvisual = 1; i++; return 1;}
+  if (argv[i][1] == 'a') {animate = 1; i++; return 1;}
   return 0;
 }
 
@@ -131,6 +145,9 @@ int main(int argc, char **argv) {
 
   Fl::args(argc,argv,i,arg);
 
+  if (animate)
+    Fl_GIF_Image::animate = true; // create animated shared .GIF images (e.g. file chooser)
+
   Fl_Double_Window window(400,450); ::w = &window;
   Fl_Box b(10,45,380,380); ::b = &b;
   b.box(FL_THIN_DOWN_BOX);
@@ -146,5 +163,7 @@ int main(int argc, char **argv) {
   svg.callback(svg_cb);
 
   window.show(argc,argv);
+  if (animate)
+    Fl::add_timeout(1./10, cb_forced_redraw); // force periodic redraw
   return Fl::run();
 }
diff --git test/pixmaps/animated_fluid_gif.h test/pixmaps/animated_fluid_gif.h
new file mode 100644
index 0000000..19d62b0
--- /dev/null
+++ test/pixmaps/animated_fluid_gif.h
@@ -0,0 +1,118 @@
+
+static const size_t animated_fluid_gif_size = 2545;
+static const unsigned char animated_fluid_gif[] =
+{71,73,70,56,57,97,96,0,96,0,132,31,0,0,1,0,3,31,63,46,48,45,0,54,108,78,80,
+77,64,123,116,124,126,123,125,173,98,107,166,116,171,204,63,220,233,19,253,254,
+0,196,218,42,147,189,83,20,101,159,44,112,135,0,88,172,0,96,191,0,109,220,91,
+161,140,68,152,169,38,140,205,0,128,255,15,133,249,45,146,236,47,148,254,108,
+182,255,164,166,162,187,222,255,202,204,201,253,255,252,0,0,0,33,255,11,78,69,
+84,83,67,65,80,69,50,46,48,3,1,0,0,0,33,249,4,5,10,0,31,0,44,0,0,0,0,96,0,96,0,
+0,5,254,224,39,142,100,105,158,104,170,174,108,235,190,112,44,207,116,109,223,
+120,110,2,124,239,3,186,160,238,71,36,14,34,61,161,18,86,244,13,32,78,139,5,201,
+91,90,87,63,143,118,171,201,112,184,25,11,180,122,45,239,120,219,180,135,147,
+241,174,219,223,164,121,46,234,169,211,108,111,87,163,149,211,205,118,119,96,28,
+93,95,28,3,100,127,101,129,130,90,95,107,26,93,22,136,138,128,104,120,28,143,
+90,124,121,97,80,149,139,151,142,109,164,111,134,153,99,160,87,140,155,145,153,
+142,142,113,137,170,66,172,130,108,90,112,126,180,65,182,119,93,163,26,136,64,
+188,181,162,183,112,111,25,169,197,189,199,119,121,143,108,204,205,57,190,120,
+25,124,91,28,187,213,54,215,120,119,221,222,52,224,141,125,179,228,229,207,231,
+105,227,234,49,230,231,239,240,47,242,141,244,245,45,247,130,249,250,88,236,218,
+161,35,246,47,222,179,78,110,196,165,43,184,239,24,194,100,106,252,49,60,193,
+232,33,41,77,3,39,50,17,101,17,226,22,137,26,73,84,36,69,50,225,199,133,33,254,
+81,140,44,233,49,99,74,22,172,186,148,212,230,14,229,203,18,172,44,98,116,121,
+51,133,47,66,26,118,158,36,216,147,98,64,129,32,95,242,83,72,180,40,206,163,
+237,124,56,61,3,245,156,128,164,19,125,16,16,40,104,131,212,169,31,180,114,109,
+100,224,171,83,31,99,149,37,236,64,192,236,77,31,27,198,178,204,48,180,105,72,
+177,92,59,62,242,138,213,155,15,1,105,231,182,217,210,182,111,51,31,29,2,11,78,
+83,216,38,60,196,105,61,8,166,91,211,49,57,31,6,34,171,37,185,147,175,229,195,
+61,0,107,222,108,146,177,219,199,61,226,142,134,228,202,234,105,191,169,87,107,
+246,108,23,52,15,213,178,35,211,86,135,55,183,230,198,181,85,193,245,189,250,
+234,103,58,195,183,93,36,126,174,195,235,74,189,37,179,100,78,246,249,159,208,
+216,88,10,165,238,1,56,45,200,91,38,115,23,100,60,120,168,219,106,196,143,103,
+106,126,73,244,92,139,215,171,41,107,184,134,143,0,24,34,233,143,116,161,191,
+255,11,251,5,40,224,128,4,6,24,128,117,198,0,254,16,128,4,82,52,232,224,131,16,
+70,40,225,132,16,74,128,224,16,61,68,64,225,134,28,118,8,33,21,199,217,215,195,
+0,30,150,104,226,132,99,132,56,67,15,1,104,248,32,6,48,62,120,193,137,52,98,
+192,160,20,7,214,215,16,15,46,54,136,65,3,12,36,160,0,2,20,76,128,0,144,61,122,
+120,65,3,9,84,112,227,139,8,28,144,0,3,10,56,41,133,4,57,182,183,98,15,16,60,88,
+129,2,11,20,48,193,2,11,8,73,38,3,17,60,217,33,2,103,166,9,33,6,19,48,64,166,2,
+110,74,145,162,150,27,241,64,226,131,114,162,201,166,2,14,200,185,64,3,117,118,
+120,193,1,115,66,160,166,131,9,144,153,64,161,98,92,104,15,139,139,82,64,38,2,
+16,68,48,1,5,16,128,25,38,164,28,126,57,36,168,82,92,32,40,166,139,102,137,39,
+76,25,202,40,232,3,105,74,32,65,5,115,58,32,43,141,18,144,42,5,173,11,0,10,42,
+150,58,62,197,67,151,47,38,170,225,140,108,150,169,40,174,183,74,152,172,2,138,
+46,58,133,164,42,254,68,225,96,5,19,52,112,38,2,19,220,216,232,2,168,74,128,65,
+145,7,80,192,32,156,155,58,152,65,5,69,54,43,5,5,9,36,48,193,183,13,44,27,225,
+48,42,10,11,192,158,14,206,235,169,2,65,106,136,129,167,176,86,32,40,153,182,
+90,128,232,160,26,82,224,233,160,79,98,208,232,144,7,23,96,111,132,170,202,224,
+131,180,22,152,74,166,197,178,74,96,105,175,209,122,188,0,154,12,10,58,65,154,
+23,12,76,230,202,231,202,233,235,152,189,218,202,177,20,17,100,105,144,158,18,
+186,188,128,3,246,46,92,47,131,38,31,160,40,175,63,187,233,51,172,82,124,91,64,
+154,52,51,144,233,134,22,246,101,109,132,35,51,96,179,20,130,62,205,32,175,10,
+192,122,65,178,82,19,157,245,212,52,39,144,169,4,218,46,96,244,205,14,226,187,
+234,125,20,38,171,246,141,24,144,249,243,178,81,71,43,232,219,82,180,253,54,6,
+42,47,75,176,174,16,234,204,42,15,27,126,139,64,157,125,223,40,184,162,35,47,0,
+235,141,93,67,109,172,5,35,67,235,238,134,254,146,250,144,100,132,135,75,78,38,
+224,25,148,254,45,180,46,130,237,128,134,223,222,205,182,163,203,194,237,224,
+157,62,177,184,97,222,100,70,219,177,167,94,115,158,168,4,19,120,58,180,20,125,
+71,112,129,167,143,139,203,252,197,29,202,109,20,15,11,110,120,118,235,181,62,
+112,128,5,52,215,27,168,227,16,32,192,224,228,13,60,160,55,200,109,91,94,0,5,38,
+2,59,142,238,28,78,126,35,205,128,54,57,187,219,129,82,208,117,3,221,90,0,252,
+1,7,144,128,167,140,118,36,48,133,13,101,237,59,141,15,136,213,184,143,65,78,
+111,10,40,128,172,210,55,36,1,206,233,81,12,122,24,6,41,152,0,243,205,233,1,159,
+235,16,238,234,48,162,14,185,204,115,13,50,216,201,130,247,165,147,193,12,81,10,
+120,27,131,254,212,128,215,137,75,91,10,168,87,4,26,197,128,203,209,104,18,82,
+185,218,134,120,165,67,7,229,42,86,13,58,34,18,143,232,174,92,133,236,74,76,12,
+89,4,88,246,67,11,232,140,82,20,194,128,147,46,64,51,31,62,232,86,79,122,148,
+226,141,158,104,196,207,49,72,2,51,234,216,25,171,104,129,170,253,32,0,27,146,
+147,248,26,37,59,54,218,241,142,86,36,2,28,41,68,166,6,208,202,87,182,195,163,
+32,39,164,42,234,109,8,1,10,152,82,15,17,55,200,70,74,168,144,10,226,144,3,40,
+96,195,16,58,242,146,247,210,99,135,66,22,72,76,98,18,146,0,128,158,39,71,121,
+34,9,224,238,141,1,72,165,42,87,201,202,86,186,242,149,176,140,165,44,103,201,
+202,31,144,176,9,184,204,165,46,119,201,203,94,246,82,36,190,12,166,48,135,73,
+76,34,76,175,152,200,76,102,49,75,16,2,0,33,249,4,5,10,0,31,0,44,30,0,4,0,38,0,
+57,0,0,5,254,224,39,142,31,96,158,104,170,2,100,235,174,112,236,206,174,103,
+223,56,158,210,60,205,113,185,91,111,152,201,136,56,25,100,50,232,25,246,126,71,
+227,143,233,57,57,121,72,13,105,169,51,93,105,154,204,141,243,225,222,172,95,
+151,70,227,209,126,128,65,116,186,85,92,127,168,115,44,156,218,204,147,236,111,
+100,83,113,94,126,128,69,101,98,132,44,115,124,124,114,126,45,123,57,145,88,102,
+56,149,60,110,153,156,157,158,159,160,161,162,149,142,120,145,165,76,164,168,
+148,167,171,152,173,174,54,170,177,125,126,180,178,176,177,179,76,28,107,26,100,
+163,34,97,69,69,192,162,74,196,197,163,201,201,155,160,204,196,206,159,208,117,
+163,195,204,198,160,200,209,66,161,189,35,147,184,78,183,169,227,228,229,78,189,
+191,231,226,62,208,225,186,154,208,108,231,150,212,240,174,238,247,236,181,36,
+219,201,217,60,253,35,22,80,224,187,96,111,0,130,226,199,176,161,67,90,219,128,
+61,196,49,144,204,68,27,3,139,93,244,144,49,137,175,143,32,67,138,244,149,225,
+130,201,147,38,3,51,132,0,0,33,249,4,5,10,0,31,0,44,34,0,7,0,34,0,54,0,0,5,202,
+224,39,138,94,105,158,168,57,174,236,154,190,104,43,143,112,237,205,179,13,227,
+178,254,242,45,95,10,200,18,198,136,52,163,10,73,82,150,152,77,39,116,74,141,42,
+167,206,39,52,123,171,122,191,224,176,120,76,46,155,207,232,244,151,195,230,156,
+57,154,140,60,227,94,107,52,245,185,190,62,229,232,229,113,127,114,85,129,130,
+134,83,1,126,130,23,134,25,136,138,127,140,135,80,137,141,134,124,76,144,127,
+112,43,28,90,80,154,114,158,30,109,163,93,125,123,58,72,92,75,64,172,39,106,177,
+76,175,63,69,175,112,119,152,46,172,133,162,61,92,161,190,182,89,141,26,65,92,
+197,199,196,134,198,195,78,189,115,186,31,172,193,205,100,112,209,68,144,120,
+102,208,116,180,90,193,25,26,224,93,222,114,229,34,231,25,233,31,25,23,240,241,
+240,119,244,245,246,247,244,33,0,33,249,4,5,10,0,31,0,44,36,0,13,0,26,0,48,0,0,
+5,196,224,39,138,156,102,114,99,170,174,100,230,186,26,43,143,220,107,163,243,
+26,104,182,157,235,188,158,235,167,218,9,135,196,84,77,24,75,166,130,55,167,
+106,153,209,224,164,88,44,103,123,205,150,162,217,207,177,155,164,246,178,208,
+51,54,237,211,30,51,225,177,103,78,247,252,190,175,79,189,46,219,115,57,123,
+124,44,129,132,123,125,133,136,42,0,139,136,137,41,139,0,141,30,93,100,31,144,
+141,102,46,28,1,131,133,98,71,14,157,132,153,47,26,156,41,152,111,149,57,164,
+154,104,76,97,164,171,101,39,78,84,86,97,31,108,25,128,146,83,71,26,146,118,79,
+111,194,42,187,46,198,196,71,202,52,114,190,191,54,193,205,74,38,86,194,216,217,
+218,219,136,102,171,194,178,43,146,173,188,42,227,170,199,214,234,214,25,23,238,
+239,238,112,41,33,0,33,249,4,5,10,0,31,0,44,38,0,5,0,21,0,56,0,0,5,232,224,39,
+142,80,52,158,104,202,101,25,151,190,98,166,105,172,11,167,44,171,125,156,125,
+139,171,26,173,245,27,245,56,195,93,17,185,11,250,96,131,97,203,89,20,205,106,
+181,170,199,99,67,102,127,219,174,231,11,3,132,129,231,159,57,204,217,186,223,
+111,145,185,7,175,199,81,109,207,76,249,91,105,182,82,79,48,109,121,71,85,64,68,
+135,39,52,127,112,76,26,130,136,132,133,57,58,47,121,34,92,149,57,28,1,153,118,
+122,155,57,3,159,118,82,155,164,31,160,161,162,25,169,171,65,155,26,175,171,167,
+53,158,170,171,154,58,79,186,160,138,192,193,194,39,190,118,55,73,160,0,0,120,
+162,121,111,2,56,173,117,27,203,70,173,45,117,204,173,206,110,218,205,198,39,
+215,191,39,177,156,197,110,73,96,231,235,117,71,189,190,167,124,240,162,74,186,
+229,149,46,246,215,249,176,251,185,160,247,56,85,177,37,67,81,188,17,231,220,17,
+99,199,176,225,186,61,16,35,74,220,19,2,0,59};
+
diff --git test/pixmaps/fltk_animated.gif test/pixmaps/fltk_animated.gif
new file mode 100644
index 0000000..bff543a
Binary files /dev/null and test/pixmaps/fltk_animated.gif differ
diff --git test/pixmaps/fltk_animated2.gif test/pixmaps/fltk_animated2.gif
new file mode 100644
index 0000000..52b8a83
Binary files /dev/null and test/pixmaps/fltk_animated2.gif differ
diff --git test/pixmaps/fltk_animated3.gif test/pixmaps/fltk_animated3.gif
new file mode 100644
index 0000000..8656c4d
Binary files /dev/null and test/pixmaps/fltk_animated3.gif differ
diff --git test/tiled_image.cxx test/tiled_image.cxx
index 54e4043..4b31382 100644
--- test/tiled_image.cxx
+++ test/tiled_image.cxx
@@ -47,11 +47,10 @@ int arg(int argc, char **argv, int &i) {
 }
 
 int main(int argc, char **argv) {
-#ifdef FLTK_USE_X11
   int i = 1;
-
   Fl::args(argc,argv,i,arg);
-
+  
+#ifdef FLTK_USE_X11
   if (visid >= 0) {
     fl_open_display();
     XVisualInfo templt; int num;
diff --git test/utf8.cxx test/utf8.cxx
index 2f2d928..8cd8763 100644
--- test/utf8.cxx
+++ test/utf8.cxx
@@ -609,7 +609,7 @@ int main(int argc, char** argv)
     end_list /= 16;
   }
   argc = 1;
-  for (long y = off; y < end_list; y++) {
+  for (int y = off; y < end_list; y++) {
     int o = 0;
     char bu[25]; // index label
     char buf[16 * 6]; // utf8 text
@@ -622,7 +622,7 @@ int main(int argc, char** argv)
       i++;
     }
     buf[o] = '\0';
-    snprintf(bu, sizeof(bu), "0x%06lX", y * 16);
+    snprintf(bu, sizeof(bu), "0x%06X", y * 16);
     Fl_Input *b = new Fl_Input(200,(y-off)*25,80,25);
     b->textfont(FL_COURIER);
     b->value(fl_strdup(bu));
Direct Link to Message ]
 
     
Previous Message ]Next Message ]
 
 

Comments are owned by the poster. All other content is copyright 1998-2024 by Bill Spitzak and others. This project is hosted by The FLTK Team. Please report site problems to 'erco@seriss.com'.