zoukankan      html  css  js  c++  java
  • 【第3版emWin教程】第22章 emWin6.x的GIF图片显示

    教程不断更新中:http://www.armbbs.cn/forum.php?mod=viewthread&tid=98429

    第22章       emWin6.x的GIF图片显示

    本期主要讲emWin支持的GIF图片显示,官方支持的主要有两种显示方法,一种方法是直接从外部存储器读取数据并显示,这种方法的好处就是不需要大的RAM,每次读取一些数据显示一次,缺点就是显示速度稍慢。另一种是从外部存储器读取整个图片到RAM(比如内部SRAM,外部SRAM或者外部SDRAM),然后再显示图片,这种方法的显示速度要稍快些。对于界面效果简单的GIF图片,两种方法区别不大,界面效果稍复杂的GIF图片,推荐后者,即将整个图片读到RAM空间,显示效果要好些。

    22.1 初学者重要提示

    22.2 GIF图片基础知识

    22.3 GIF图片的API函数及其显示方法

    22.4 实验例程说明(RTOS)

    22.5 实验例程说明(裸机)

    22.6 总结

    22.1 初学者重要提示

    1、  GIF图片显示的所有API函数在emWin手册中都有讲解,下图是中文版手册里面API函数的位置

     

    下图是英文版手册里面API函数的位置:

     

    2、  本章教程使用的外部存储器是SD卡,实际项目中使用任何其它类型的存储器都可以的,支不支持文件系统都没有关系的,使用方法与本章教程一样,用户要做的就是把图片从外部存储器读出即可。

    22.2 GIF图片基础知识

    关于GIF图片格式方面的知识,推荐大家看wiki百科上面的介绍:

    推荐初学者了解一下GIF文件的格式,如果没有了解也是没有任何关系的,直接调用emWin的API

    函数就可以显示GIF图片了。

    ----------------------------------------------------------------------------------------------------------

    下面这点小知识还是要知道的:

    GIF(Graphics Interchange Format)的原义是“图像互换格式”,是CompuServe公司在 1987年开发的图像文件格式。GIF文件的数据,是一种基于LZW算法的连续色调的无损压缩格式。其压缩率一般在50%左右,它不属于任何应用程序。目前几乎所有相关软件都支持它,公共领域有大量的软件在使用GIF图像文件。GIF图像文件的数据是经过压缩的,而且是采用了可变长度等压缩算法。GIF格式的另一个特点是其在一个GIF文件中可以存多幅彩色图像,如果把存于一个文件中的多幅图像数据逐幅读出并显示到屏幕上,就可构成一种最简单的动画。

    GIF格式自1987年由CompuServe公司引入后,因其体积小且成像相对清晰,特别适合于初期慢速的互联网,而从此大受欢迎。它采用无损压缩技术,只要图像不多于256色,则可既减少文件的大小,又保持成像的质量。(当然,现在也存在一些hack技术,在一定的条件下克服了256色的限制)然而,256色的限制大大局限了GIF文件的应用范围,如彩色相机等。(当然采用无损压缩技术的彩色相机照片亦不适合通过网络传输。)另一方面,在高彩图片上有着不俗表现的JPG格式却在简单的折线效果上差强人意。因此GIF格式普遍适用于图表,按钮等等只需少量颜色的图像(如黑白照片)。

    22.3 GIF图片的API函数及其显示方法

    当前emWin支持的API函数有如下16个:

    从上面的表格中可以看出,emWin支持GIF文件显示主要有两种类型的函数,一类是以Ex结尾的函数,这种函数显示GIF图片是一边从外部存储器加载数据一边显示,显示速度相对较慢,适用于内存较小的场合。另一类是不以Ex结尾的函数,这种函数直接从指定的地址读取数据进行显示(注意,这里的地址需是总线式地址,比如外部SDRAM,外部SRAM,内部Flash和内部SRAM都可以),显示速度相对稍快。

    本章教程会对这两种方式都进行说明:

    •   int GUI_GIF_DrawSub(const void * pGIF, U32 NumBytes, int x0, int y0, int Index);

    此函数直接从地址pGIF读取第Index张(注意,这里的Index就是指的参数Index所指定的子图片序号,这个参数是从0开始计数的)GIF子图片的数据,将图片显示到用户设置的位置(x0, y0)。

    •   int GUI_GIF_DrawSubEx(GUI_GET_DATA_FUNC * pfGetData, void * p, int x0, int y0, int Index);

    此函数通过其回调函数pfGetData读取第Index张(注意,这里的Index就是指的参数Index所指定的子图片序号,这个参数是从0开始计数的)GIF子图片的数据,从而实现边读取图片数据边显示的功能,将图片显示到用户设置的位置(x0, y0)。

    实现GIF图片的动态显示还要用如下四个函数配合:

    •   int GUI_GIF_GetInfo(const void * pGIF, U32 NumBytes, GUI_GIF_INFO * pInfo);

    此函数直接从地址pGIF读取GIF文件数据,返回GIF图片的长度,宽度以及子图片的张数。返回结果是存储到第三个参数所指向的结构体变量地址。

    •   int GUI_GIF_GetInfoEx(GUI_GET_DATA_FUNC * pfGetData, void * p, GUI_GIF_INFO * pInfo);

    此函数通过其回调函数pfGetData读取图片数据,返回GIF图片的长度,宽度以及子图片的张数。返回结果是存储到第三个参数所指向的结构体变量地址。结构体GUI_GIF_INFO的定义如下(函数GUI_GIF_GetInfo和函数GUI_GIF_GetInfoEx的结构体类型是一样的):

     

    这个里面有个错误:不是XSize和YSize,而是xSize和ySize(这个错误官方手册一直没有更正)。

    •   int GUI_GIF_GetImageInfo(const void * pGIF, U32 NumBytes, GUI_GIF_IMAGE_INFO * pInfo, int Index);

    此函数直接从地址pGIF读取GIF文件数据,返回上次(注意,这里的上次就是指的参数Index所指定的子图片序号,这个参数是从0开始计数的)所绘制的子图片显示位置,长度,宽度和显示多长时间。其中显示多少时间的参数比较有用,专门用于设置各个子图片之间的时间间隔。返回结果是存储到第三个参数所指向的结构体变量地址。

    •   Int GUI_GIF_GetImageInfoEx(GUI_GET_DATA_FUNC * pfGetData, void * p,

                                GUI_GIF_IMAGE_INFO * pInfo, int Index);

    此函数通过其回调函数pfGetData读取图片数据,返回上次(注意,这里的上次就是指的参数Index所指定的子图片序号,这个参数是从0开始计数的)所绘制的子图片显示位置,长度,宽度和显示多长时间。其中显示多少时间的参数比较有用,专门用于设置时间各个子图片之间的时间间隔。返回结果是存储到第三个参数所指向的结构体变量地址。结构体GUI_GIF_IMAGE_INFO的定义如下(函数GUI_GIF_GetImageInfo和GUI_GIF_GetImageInfoEx的结构体类型是一样的):

     

    22.3.1   绘制已经加载到存储器的GIF图片

    绘制加载到存储器的GIF图片主要是通过函数GUI_GIF_DrawSub来实现,下面我们分2步来说明如何将SD卡中的GIF图片显示到LCD上面。

    •   第1步:将GIF图片复制到SD卡的根目录下,然后通过emWin的动态内存管理函数申请动态内存并将GIF文件加载进来, 这里我们用的是外部SDRAM做emWin的动态内存。
    char *_acBuffer;
        GUI_HMEM hMem;
    
        /* 打开文件 */        
        result = f_open(&file, sFilename, FA_OPEN_EXISTING | FA_READ | FA_OPEN_ALWAYS);
        if (result != FR_OK)
        {
            return 0;
        }
         
        /* 申请一块内存空间 并且将其清零 */
        hMem = GUI_ALLOC_AllocZero(file.obj.objsize);
        
        /* 将申请到内存的句柄转换成指针类型 */
        _acBuffer = GUI_ALLOC_h2p(hMem);
    
        /* 读取文件到动态内存 */
        result = f_read(&file, _acBuffer, file.obj.objsize, &bw);
        if (result != FR_OK)
        {
            return 0;
        }
    •   第2步:将加载到emWin动态内存的GIF图片直接显示即可,特别注意每张子图片的显示方法及其时间间隔设置,然后结合第1步,完整的代码如下:
    /*
    *********************************************************************************************************
    *    函 数 名: _ShowGIF2
    *    功能说明: 显示GIF片
    *    形    参: sFilename 要显示的图片名字
    *    返 回 值: 无
    *********************************************************************************************************
    */
    void _ShowGIF2(const char * sFilename) 
    {
        uint16_t i = 0;
        uint32_t t0, t1;
        char *_acBuffer;
        GUI_HMEM hMem;
        char buf[50];
    
        /* 打开文件 */        
        result = f_open(&file, sFilename, FA_OPEN_EXISTING | FA_READ | FA_OPEN_ALWAYS);
        if (result != FR_OK)
        {
            return;
        }
         
        /* 申请一块内存空间 并且将其清零 */
        hMem = GUI_ALLOC_AllocZero(file.obj.objsize);
        
        /* 将申请到内存的句柄转换成指针类型 */
        _acBuffer = GUI_ALLOC_h2p(hMem);
    
        /* 读取文件到动态内存 */
        result = f_read(&file, _acBuffer, file.obj.objsize, &bw);
        if (result != FR_OK)
        {
            return;
        }
       
        /* 获取GIF图片信息 */
       GUI_GIF_GetInfo(_acBuffer, file.obj.objsize, &InfoGif);
        
       while(1)
       {
            /* 变量用来设置当前播放的帧数,InfoGif.NumImages是GIF图片总的帧数 */
            if(i < InfoGif.NumImages)
            {
                /* 获取当前帧GIF图片信息,注意第4个参数是从0开始计数的 */
                GUI_GIF_GetImageInfo(_acBuffer, file.obj.objsize, &ImagInfoGif, i);
                
                /* 如果此帧延迟时间是0,默认是延迟100ms */
                if(ImagInfoGif.Delay == 0)
                {
                    GUI_Delay(100);
                }
                else
                {
                    t0 = GUI_GetTime();
                    /* 显示当前播放的帧数 */
                    sprintf(buf, "     Frame:%d/%d     ", i+1, InfoGif.NumImages);
                    GUI_DispStringHCenterAt(buf, LCD_GetXSize()/2, 0);
                    
                    /* 解码并显示此帧GIF图片,注意第5个参数是从0开始计数的 */
                    GUI_GIF_DrawSub(_acBuffer, 
                                    file.obj.objsize, 
                                    (LCD_GetXSize() - InfoGif.xSize)/2,
                                    (LCD_GetYSize() - InfoGif.ySize)/2, 
                                    i++);
                    
                    /* 获取本次解码和显示消耗的时间 */
                    t1 = GUI_GetTime() - t0;
                    
                    /* 如果GIF的解码和显示的时间超时就不做延迟 */
                    if (t1 < ImagInfoGif.Delay * 10) 
                    {
                        GUI_Delay(ImagInfoGif.Delay * 10 - t1);
                    }
                }              
            }
            else
            {
                i = 0;
            }
        }
    
        /* 实际应用中,根据实际情况释放动态内存 */        
    //     GUI_ALLOC_Free(hMem);
    //     f_close(&file);
    }

    通过上面两步就完成了GIF图片的动态显示,用户要显示哪个图片,调用函数_ShowGIF2()即可,比如要显示1.gif图片,可以调用_ShowGIF2("1.gif"),这种方式显示GIF图片相对稍快些。实际显示效果参看本章节配套的实验例程说明。

    22.3.2   绘制无需加载到存储器的GIF图片

    绘制无需加载到存储器的GIF图片主要是通过函数GUI_GIF_DrawSubEx来实现,这种方式的优点是需要的内存小,但是显示速度稍慢。下面我们分2步来说明如何将SD卡中的GIF图片显示到LCD上面。

    • 第1步:将GIF图片复制到SD卡的根目录下,然后直接调用函数GUI_GIF_DrawSubEx就可以显示。
    /*
    *********************************************************************************************************
    *    函 数 名: _GetData
    *    功能说明: 被函数GUI_BMP_DrawEx调用
    *    形    参:p             FIL类型数据
    *             NumBytesReq   请求读取的字节数
    *             ppData        数据指针
    *             Off           如果Off = 1,那么将重新从起始位置读取                 
    *    返 回 值: 返回读取的字节数
    *********************************************************************************************************
    */
    int _GetData(void * p, const U8 ** ppData, unsigned NumBytesReq, U32 Off) 
    {
        static int FileAddress = 0;
        UINT NumBytesRead;
        FIL *PicFile;
    
        PicFile = (FIL *)p;
    
        /*
        * 检测缓存大小
        */
        if (NumBytesReq > sizeof(acBuffer)) {
        NumBytesReq = sizeof(acBuffer);
        }
    
        /*
        * 设置读取位置
        */
        if(Off == 1) FileAddress = 0;
        else FileAddress = Off;
        result =f_lseek(PicFile, FileAddress);
    
        /*
        * 读取数据到缓存
        */
        result = f_read(PicFile, acBuffer, NumBytesReq, &NumBytesRead);
    
        /*
        * 让指针ppData指向读取的数据
        */
        *ppData = (const U8 *)acBuffer;
    
        /*
        * 返回读取的字节数
        */
        return NumBytesRead;
    }
    
    /*
    *********************************************************************************************************
    *    函 数 名: _ShowGIF1
    *    功能说明: 显示GIF片
    *    形    参: sFilename 要显示的图片名字
    *    返 回 值: 无
    *********************************************************************************************************
    */
    void _ShowGIF1(const char * sFilename) 
    {
        uint16_t i = 0;
        uint32_t t0, t1;
        char buf[50];
    
        /* 打开文件 */        
        result = f_open(&file, sFilename, FA_OPEN_EXISTING | FA_READ | FA_OPEN_ALWAYS);
        if (result != FR_OK)
        {
            return;
        }
       
        /* 获取GIF图片信息 */
       GUI_GIF_GetInfoEx(_GetData, &file,&InfoGif);
        
       while(1)
       {
            /* 变量用来设置当前播放的帧数,InfoGif.NumImages是GIF图片总的帧数 */
            if(i < InfoGif.NumImages)
            {
                /* 获取当前帧GIF图片信息,注意第4个参数是从0开始计数的 */
                GUI_GIF_GetImageInfoEx(_GetData, &file, &ImagInfoGif, i);
                
                /* 如果此帧延迟时间是0,默认是延迟100ms */
                if(ImagInfoGif.Delay == 0)
                {
                    GUI_Delay(100);
                }
                else
                {
                    t0 = GUI_GetTime();
                    /* 显示当前播放的帧数 */
                    sprintf(buf, "     Frame:%d/%d     ", i+1, InfoGif.NumImages);
                    GUI_DispStringHCenterAt(buf, LCD_GetXSize()/2, 0);
                    
                    /* 解码并显示此帧GIF图片,注意第5个参数是从0开始计数的 */
                    GUI_GIF_DrawSubEx(_GetData, 
                                      &file, 
                                      (LCD_GetXSize() - InfoGif.xSize)/2,
                                      (LCD_GetYSize() - InfoGif.ySize)/2, 
                                      i++);
                    
                    /* 获取本次解码和显示消耗的时间 */
                    t1 = GUI_GetTime() - t0;
                    
                    /* 如果GIF的解码和显示的时间超时就不做延迟 */
                    if (t1 < ImagInfoGif.Delay * 10) 
                    {
                        GUI_Delay(ImagInfoGif.Delay * 10 - t1);
                    }
                }              
            }
            else
            {
                i = 0;
            }
        }
    
        /* 实际应用中,根据实际情况释放动态内存 */        
    //     GUI_ALLOC_Free(hMem);
    //     f_close(&file);
    }
    • 第2步:用户要显示指定的文件1.gif,调用函数_ShowGIF1("1.gif")即可显示。

    通过上面2步就完成了GIF图片的动态显示,这种方式显示GIF图片速度稍慢,实际显示效果参看本章节配套的实验例程说明。

    22.4 实验例程说明(RTOS)

    配套例子:

    V7-526_emWin6.x实验_GIF图片显示(RTOS)

    实验目的:

    1. 学习emWin的GIF图片显示。
    2. emWin功能的实现在MainTask.c文件里面。

    实验注意:

    1. 本实验主要学习emWin的GIF图片显示功能,实验所需的图片文件1.gif已经存储到本工程的Doc文件夹下,使用此例子前,请务必将此文件存储到SD卡根目录中,并将SD卡插到开发板上面。

    实验内容:

    1、K1按键按下,串口或者RTT打印任务执行情况(串口波特率115200,数据位8,奇偶校验位无,停止位1)。

    2、(1) 凡是用到printf函数的全部通过函数App_Printf实现。

        (2) App_Printf函数做了信号量的互斥操作,解决资源共享问题。

    3、默认上电是通过串口打印信息,如果使用RTT打印信息:

    MDK AC5,MDK AC6或IAR通过使能bsp.h文件中的宏定义为1即可

    #define Enable_RTTViewer  1

    4、各个任务实现的功能如下:

    App Task Start   任务 :启动任务,这里用作BSP驱动包处理。

    App Task MspPro任务 :消息处理,这里用作LED闪烁。

    App Task UserIF  任务 :按键消息处理。

    App Task COM   任务 :暂未使用。

    App Task GUI    任务 :GUI任务。

    μCOS-III任务调试信息(按K1按键,串口打印):

     

    RTT 打印信息方式:

     

    程序设计:

    任务栈大小分配:

    μCOS-III任务栈大小在app_cfg.h文件中配置:

    #define  APP_CFG_TASK_START_STK_SIZE                      512u

    #define  APP_CFG_TASK_MsgPro_STK_SIZE                     2048u

    #define  APP_CFG_TASK_COM_STK_SIZE                        512u

    #define  APP_CFG_TASK_USER_IF_STK_SIZE                    512u

    #define  APP_CFG_TASK_GUI_STK_SIZE                        2048u

    任务栈大小的单位是4字节,那么每个任务的栈大小如下:

    App Task Start   任务 :2048字节。

    App Task MspPro任务 :8192字节。

    App Task UserIF  任务 :2048字节。

    App Task COM   任务 :2048字节。

    App Task GUI    任务 :8192字节。

    系统栈大小分配:

    μCOS-III的系统栈大小在os_cfg_app.h文件中配置:

    #define  OS_CFG_ISR_STK_SIZE                      512u     

    系统栈大小的单位是4字节,那么这里就是配置系统栈大小为2KB

    emWin动态内存配置:

    GUIConf.c文件中的配置如下:

    #define EX_SRAM   1/*1 used extern sram, 0 used internal sram */
    
    #if EX_SRAM
    #define GUI_NUMBYTES  (1024*1024*24)
    #else
    #define GUI_NUMBYTES  (100*1024)
    #endif

    通过宏定义来配置使用内部SRAM还是外部的SDRAM做为emWin的动态内存,当配置:

    #define  EX_SRAM     1 表示使用外部SDRAM作为emWin动态内存,大小24MB。

    #define  EX_SRAM     0 表示使用内部SRAM作为emWin动态内存,大小100KB。

    默认情况下,本教程配套的所有emWin例子都是用外部SDRAM作为emWin动态内存。

    emWin界面显示效果:

    800*480分辨率界面效果。

     

    22.5 实验例程说明(裸机)

    配套例子:

    V7-525_emWin6.x实验_GIF图片显示(裸机)

    实验目的:

    1. 学习emWin的BMP图片显示。
    2. emWin功能的实现在MainTask.c文件里面。

    实验注意:

    1. 本实验主要学习emWin的GIF图片显示功能,实验所需的图片文件1.gif已经存储到本工程的Doc文件夹下,使用此例子前,请务必将此文件存储到SD卡根目录中,并将SD卡插到开发板上面。

    emWin界面显示效果:

    800*480分辨率界面效果。

     

    emWin动态内存配置:

    GUIConf.c文件中的配置如下:

    #define EX_SRAM   1/*1 used extern sram, 0 used internal sram */
    
    #if EX_SRAM
    #define GUI_NUMBYTES  (1024*1024*24)
    #else
    #define GUI_NUMBYTES  (100*1024)
    #endif

    通过宏定义来配置使用内部SRAM还是外部的SDRAM做为emWin的动态内存,当配置:

    #define  EX_SRAM     1 表示使用外部SDRAM作为emWin动态内存,大小24MB。

    #define  EX_SRAM     0 表示使用内部SRAM作为emWin动态内存,大小100KB。

    默认情况下,本教程配套的所有emWin例子都是用外部SDRAM作为emWin动态内存。

    22.6 总结

    对于比较简单的GIF图片,使用本章节提供的两种方法都是可以的,显示效果差不多,但是对于界面效果稍复杂些的GIF图片,推荐将整个GIF图片加载的SDRAM或者SRAM中再显示,效果比较好。

    微信公众号:armfly_com 安富莱论坛:www.armbbs.cn 安富莱淘宝:https://armfly.taobao.com
  • 相关阅读:
    可能是最简单的解决本地开发接口请求跨域问题的方案
    《Visual Studio程序员箴言》笔记
    vue后台项目记录
    新版本微信导致的ios表单bug
    longzhuapp项目笔记
    Session、LocalStorage、SessionStorage、Cache-Ctrol比较
    css3实现不同进度条
    axios请求接口的踩坑之路
    实现不同尺寸的图片在固定的区块内实现水平垂直居中
    for循环查找元素怎么跳出for循环
  • 原文地址:https://www.cnblogs.com/armfly/p/14959372.html
Copyright © 2011-2022 走看看