summaryrefslogtreecommitdiff
path: root/examples/animgifimage-play.cxx
diff options
context:
space:
mode:
authorwcout <wcout@users.noreply.github.com>2023-01-21 17:27:58 +0100
committerGitHub <noreply@github.com>2023-01-21 17:27:58 +0100
commit2ddfd9d9492d9fc1df111ec9211dd1be4d424c35 (patch)
treec766d0dfb3a2d7a75c275db2821d5bcf0e935a15 /examples/animgifimage-play.cxx
parent1fc269b0d4c79b256cc57740d318f95dded8c340 (diff)
Animated GIF support (Fl_Anim_GIF_Image class) (#375)
Diffstat (limited to 'examples/animgifimage-play.cxx')
-rw-r--r--examples/animgifimage-play.cxx237
1 files changed, 237 insertions, 0 deletions
diff --git a/examples/animgifimage-play.cxx b/examples/animgifimage-play.cxx
new file mode 100644
index 000000000..4a319f842
--- /dev/null
+++ b/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();
+}