Animated GIF support (Fl_Anim_GIF_Image class) (#375)

This commit is contained in:
wcout 2023-01-21 17:27:58 +01:00 committed by GitHub
parent 1fc269b0d4
commit 2ddfd9d949
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
32 changed files with 3082 additions and 472 deletions

185
FL/Fl_Anim_GIF_Image.H Normal file
View File

@ -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

View File

@ -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

View File

@ -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();

View File

@ -81,6 +81,10 @@ set (FLUID_SOURCES
############################################################
set (IMAGE_SOURCES
animgifimage
animgifimage-play
animgifimage-resize
animgifimage-simple
howto-simple-svg
)

View File

@ -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) \

View 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();
}

View 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();
}
}

View 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
View 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();
}

View File

@ -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;
}

View File

@ -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;

View File

@ -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);

View File

@ -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 {} {

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

@ -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

1279
src/Fl_Anim_GIF_Image.cxx Normal file

File diff suppressed because it is too large Load Diff

View File

@ -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
@ -73,6 +73,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.
@ -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;
load_gif_(rdr, anim);
}
// 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
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 {
Fl::error("%s: unknown GIF code 0x%02x at offset %ld", rdr.name(), i, rdr.tell()-1);
ld(ERR_FORMAT); // broken file
return; // terminate
load_gif_(rdr, anim);
}
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());
}
/**
The default constructor creates an empty GIF image.
// read image data
*/
Fl_GIF_Image::Fl_GIF_Image() :
Fl_Pixmap((char *const*)0)
{
}
// printf("Image Data at offset %ld\n", rdr.tell());
CodeSize = rdr.read_byte() + 1; // LZW Minimum Code Size
CHECK_ERROR
/*
Internally used method to read from the LZW compressed data
stream 'rdr' and decode it to 'Image' buffer.
if (BitsPerPixel >= CodeSize) { // Workaround for broken GIF files...
BitsPerPixel = CodeSize - 1;
ColorMapSize = 1 << BitsPerPixel;
}
// 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));
}
}
// 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;
}
#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
CHECK_ERROR
// now read the LZW compressed image data
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;
@ -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
w(Width);
h(Height);
d(1);
/*
Internally used function to convert raw 'Image' data
to XPM format in an allocated buffer 'new_data'.
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;
}
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());
}
} // 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);
}
}

View File

@ -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 \

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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 $@...

View File

@ -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;

View File

@ -263,6 +263,9 @@ Nested OL/UL:
</TR><TR>
<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>

View File

@ -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);

View File

@ -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();
}

View File

@ -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};

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

View File

@ -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;

View File

@ -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));