Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Animated GIF support (Fl_Anim_GIF_Image class) #375

Merged
merged 14 commits into from
Jan 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
185 changes: 185 additions & 0 deletions FL/Fl_Anim_GIF_Image.H
Original file line number Diff line number Diff line change
@@ -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
45 changes: 43 additions & 2 deletions FL/Fl_GIF_Image.H
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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
3 changes: 3 additions & 0 deletions FL/Fl_Image.H
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
4 changes: 4 additions & 0 deletions examples/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,10 @@ set (FLUID_SOURCES
############################################################

set (IMAGE_SOURCES
animgifimage
animgifimage-play
animgifimage-resize
animgifimage-simple
howto-simple-svg
)

Expand Down
6 changes: 5 additions & 1 deletion examples/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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) \
Expand Down