summaryrefslogtreecommitdiff
path: root/examples/animgifimage.cxx
blob: 515b76b65162f0e173c2238a4b15b232f471726b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
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();
}