当前位置: 首页 > article >正文

FLTK - FLTK1.4.1 - demo - animgifimage

文章目录

    • FLTK - FLTK1.4.1 - demo - animgifimage
    • 概述
    • 笔记
    • END

FLTK - FLTK1.4.1 - demo - animgifimage

概述

知识点:
注册图像文件类型判断回调
FLTK支持的图像格式 GIF, BMP, ICO, PNM, PNG, jpg, svg
事件回调的注册
GIF图像显示为图片或动画的标志设置
// 超时回调的设置和移除, 超时回调是一次性的,如果还需要同一个超时回调,需要再次设置超时回调
// 文件选择器UI的调用和返回
// 用户选择通过文件选择器选择的文件,有可能不是指定尾缀的文件类型
// fltk窗口的遍历
// fltk窗口 背景颜色设置 Fl_Color
// fltk窗口退出回调的设置
// 图像数据载入成功和图像数据有效的判断
// 图像关键数据(长,宽,帧数)的取得
// fltk窗口类的copy_x()是复制了一份数据,并不是指向原始数据
// fltk图像类禁用缓存
// fltk窗体禁止响应快捷键的窗体size缩放
// 等待当前窗体正常结束
// fltk类的构造函数是通过弹窗来报错的(e.g. 图像文件格式错), 这个报错信息不是API调用者能控制的。

笔记

// FLTK - FLTK1.4.1 - demo - animgifimage
/*
知识点:
    注册图像文件类型判断回调
    FLTK支持的图像格式 GIF, BMP, ICO, PNM, PNG, jpg, svg
    事件回调的注册
    GIF图像显示为图片或动画的标志设置
    // 超时回调的设置和移除, 超时回调是一次性的,如果还需要同一个超时回调,需要再次设置超时回调
    // 文件选择器UI的调用和返回
    // 用户选择通过文件选择器选择的文件,有可能不是指定尾缀的文件类型
    // fltk窗口的遍历
    // fltk窗口 背景颜色设置 Fl_Color
    // fltk窗口退出回调的设置
    // 图像数据载入成功和图像数据有效的判断
    // 图像关键数据(长,宽,帧数)的取得
    // fltk窗口类的copy_x()是复制了一份数据,并不是指向原始数据
    // fltk图像类禁用缓存
    // fltk窗体禁止响应快捷键的窗体size缩放
    // 等待当前窗体正常结束
    // fltk类的构造函数是通过弹窗来报错的(e.g. 图像文件格式错), 这个报错信息不是API调用者能控制的。
*/

#include "fltk_test.h"

// 如果要将fl demo的实现搬过来测试,就注释掉下面的宏
// #define DONT_USE_FL_DEMO

#ifdef DONT_USE_FL_DEMO
int fl_demo_main(int argc, char** argv)
{
	return 0;
}

#else

#endif // TEST_FL_DEMO

//
//  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 = 30 /* 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");

    // fltk窗口类的copy_x()是复制了一份数据,并不是指向原始数据
    win->copy_label(buf);
    win->copy_tooltip(buf);
}

static void cb_forced_redraw(void* d) {
    // fltk窗口的遍历
    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);

    // fltk窗口 背景颜色设置 Fl_Color
    win->color(BackGroundColor);
    if (close)
    {
        // fltk窗口退出回调的设置
        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
    // fltk类的构造函数是通过弹窗来报错的(e.g. 图像文件格式错), 这个报错信息不是API调用者能控制的。
    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
    // fltk图像类禁用缓存
    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());
            }
        }
        // fltk窗体禁止响应快捷键的窗体size缩放
        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 fl_demo_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
        // GIF图像显示为图片或动画的标志设置
        Fl_GIF_Image::animate = true; // create animated shared .GIF images (e.g. file chooser)
        while (1) {
            Fl::add_timeout(RedrawDelay, cb_forced_redraw); // animate images in chooser

            // 文件选择器UI的调用和返回
            // 用户选择通过文件选择器选择的文件,有可能不是指定尾缀的文件类型
            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();
}

END


http://www.kler.cn/a/523539.html

相关文章:

  • HttpClient学习
  • 17、Spring MVC 框架:构建强大的 Java Web 应用程序
  • MotionLCM 部署笔记
  • mysql.sock.lock 导致mysql重启失败
  • 获取snmp oid的小方法1(随手记)
  • 51单片机开发:独立键盘实验
  • 漂亮数 (线性筛+前缀和)
  • 【小白学AI系列】NLP 核心知识点(五)Transformer介绍
  • 99.19 金融难点通俗解释:营业总收入vs归母净利润vs扣非净利润
  • 新鲜速递:DeepSeek-R1开源大模型本地部署实战—Ollama + MaxKB 搭建RAG检索增强生成应用
  • 数论问题75
  • LeetCode题练习与总结:N 叉树的后序遍历--590
  • 2025年AI Agent(智能体)的发展机会
  • C语言连接Mysql
  • PCIe基础分享
  • TensorFlow实现逻辑回归模型
  • 本地部署 DeepSeek-R1 大模型指南:基于 Ollama 的完整流程
  • Cyber Security 101-Build Your Cyber Security Career-Security Principles(安全原则)
  • 软件工程-软件开发模型
  • RoboMaster- RDK X5能量机关实现案例(一)识别
  • .~C#循环结构
  • Vue学习四—— Home主体页面
  • 数据结构与算法分析:专题内容——人工智能中的寻路4之A*搜索(代码详解)
  • 智慧园区系统分类及其在提升企业管理效率中的创新应用探讨
  • 软件工程概论试题一
  • 服务器上安装Nginx详细步骤