mirror of
https://github.com/fltk/fltk.git
synced 2025-12-08 11:21:13 +08:00
Animated GIF support (Fl_Anim_GIF_Image class) (#375)
This commit is contained in:
@@ -81,6 +81,10 @@ set (FLUID_SOURCES
|
||||
############################################################
|
||||
|
||||
set (IMAGE_SOURCES
|
||||
animgifimage
|
||||
animgifimage-play
|
||||
animgifimage-resize
|
||||
animgifimage-simple
|
||||
howto-simple-svg
|
||||
)
|
||||
|
||||
|
||||
@@ -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) \
|
||||
|
||||
237
examples/animgifimage-play.cxx
Normal file
237
examples/animgifimage-play.cxx
Normal file
@@ -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();
|
||||
}
|
||||
185
examples/animgifimage-resize.cxx
Normal file
185
examples/animgifimage-resize.cxx
Normal file
@@ -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();
|
||||
}
|
||||
}
|
||||
40
examples/animgifimage-simple.cxx
Normal file
40
examples/animgifimage-simple.cxx
Normal file
@@ -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();
|
||||
}
|
||||
303
examples/animgifimage.cxx
Normal file
303
examples/animgifimage.cxx
Normal file
@@ -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();
|
||||
}
|
||||
Reference in New Issue
Block a user