嵌入式Linux下LCD应用编程: 调用giflib库解码显示GIF动态图

简介: 嵌入式Linux下LCD应用编程: 调用giflib库解码显示GIF动态图

一、开发环境介绍

开发板:友善之臂Tiny4412


LCD型号: S702 、分辨率: 800*480


Linux内核版本: Linux 3.5


交叉编译器: arm-linux-gcc 4.5.1


二、GIF文件格式简单介绍

生活中常用图片格式有BMP、PNG、JPG、GIF等。BMP图片的显示很简单,可以直接从图片文件里读取RGB数据进行显示.。PNG格式图片显示,直接调用libpng库里的接口函数解码显示;JPG格式图片也一样,调用libjpeg库的接口函数完成解码即可得到原始RGB数据完成显示;如果要在LCD屏上显示GIF图片,那么也是调用giflib库的接口函数完成解码显示。


在解码jpeg图片和png图片的时候我们不需要对jpeg和png文件格式有了解就可以解码了(了解jpeg和png当然更好),但是在使用giflib解码gif的时候,我们必须要对gif文件有很简单的了解。


gif文件中可以存放一帧或者多帧图像数据,并且可以存放图像控制信息,因此可以存储动画图片,gif文件由文件头开头,文件尾结尾,中间是一些连续的数据块(block)。这些数据块又分为图像数据块和扩展数据块(extension),图像数据块可以理解成存放一帧的图像数据。扩展数据块存放的是一些辅助信息,比如指示怎样显示图像数据等等。


gif文件中的图像基于调色板的,因此一张gif文件中的图像最多只能有255中颜色,因此gif文件只能存储比较简单的图像。gif文件中有两种调色板 ——全局调色板和图像局部调色板。当一帧图像有局部调色板时,则以局部调色板来解码该帧图像,如果该帧图像没有局部调色板则用全局调色板来解码该图像。


更详细的信息可以查阅giflib的文档中的gif89.txt文件,或者在网络搜索相关的信息。


三、移植giflib库到嵌入式Linux平台

giflib库下载地址: http://www.linuxfromscratch.org/blfs/view/svn/general/giflib.html

image.png

编译过程:  

[wbyq@wbyq work]$ tar xvf /mnt/hgfs/linux-share-dir/giflib-5.2.1.tar.gz
[wbyq@wbyq work]$ cd giflib-5.2.1/
[wbyq@wbyq giflib-5.2.1]$ make CC=arm-linux-gcc
[wbyq@wbyq giflib-5.2.1]$ make PREFIX=$PWD/_install install
[wbyq@wbyq giflib-5.2.1]$ tree _install/
_install/
├── bin
│   ├── gif2rgb
│   ├── gifbuild
│   ├── gifclrmp
│   ├── giffix
│   ├── giftext
│   └── giftool
├── include
│   └── gif_lib.h
├── lib
│   ├── libgif.a
│   ├── libgif.so -> libgif.so.7
│   ├── libgif.so.7 -> libgif.so.7.2.0
│   └── libgif.so.7.2.0
└── share
    └── man
        └── man1
            ├── gif2rgb.1
            ├── gifbg.1
            ├── gifbuild.1
            ├── gifclrmp.1
            ├── gifcolor.1
            ├── gifecho.1
            ├── giffix.1
            ├── gifhisto.1
            ├── gifinto.1
            ├── giflib.1
            ├── giftext.1
            ├── giftool.1
            └── gifwedge.1
6 directories, 24 files
[wbyq@wbyq giflib-5.2.1]$

编译完成之后,将头文件和库文件拷贝一份到交叉编译器的路径下,方便程序编译时直接可以找到头文件和库文件;当前,也可以在程序编译的时候在编译器后面指定gif使用的头文件和库文件也可以。再将动态库文件拷贝一份到目标开发板的lib目录下,方便程序在开发板上运行时能找到对应的库。

[wbyq@wbyq _install]$ pwd
/home/wbyq/work/giflib-5.2.1/_install
[wbyq@wbyq _install]$ ls
bin  include  lib  share
[wbyq@wbyq _install]$ sudo cp include/* /home/wbyq/work/arm-linux-gcc/opt/FriendlyARM/toolschain/4.5.1/arm-none-linux-gnueabi/sys-root/usr/include/ -rf
[wbyq@wbyq _install]$ sudo cp lib/* /home/wbyq/work/arm-linux-gcc/opt/FriendlyARM/toolschain/4.5.1/arm-none-linux-gnueabi/sys-root/usr/lib/ -rf
[wbyq@wbyq _install]$ cp lib/* /home/wbyq/work/rootfs/lib/

四、示例代码

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/fb.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdlib.h>
#include <gif_lib.h>
struct fb_var_screeninfo var; //可变参数
struct fb_fix_screeninfo fix; //固定参数
unsigned char *fb_mem=NULL;   //LCD屏的首地址
/*
函数功能: 画点
*/
void LCD_WritePoint(int x,int y,int color)
{
  unsigned int *lcd=(unsigned int *)(fb_mem+y*var.xres*var.bits_per_pixel/8+x*var.bits_per_pixel/8);
  *lcd=color; //颜色赋值
}
//帧缓冲显示
void FrameBufferDraw(int x,int y,int image_w,int image_h,unsigned char *rgbBuf)
{
  int w,h;
  unsigned char r,g,b; 
  unsigned int c;
  /*将图像数据显示在LCD屏幕上*/
  unsigned char *rgb_p=rgbBuf;
  for(h=0;h<image_h;h++)
  {
    for(w=0;w<image_w;w++)
    {
      b=*rgb_p++;
      g=*rgb_p++;
      r=*rgb_p++;
      c=r<<16|g<<8|b<<0;
      LCD_WritePoint(w+x,h+y,c); /*绘制像素点到LCD屏*/
    }
  }
}
//颜色转换
void GifBufferToRgb888(ColorMapObject *ColorMap, unsigned char *inRgb,GifRowType *Buffer, int w, int h)
{
    GifColorType *ColorMapEntry = NULL;
    GifRowType GifRow = NULL;
    unsigned char *rgbBuf = inRgb;
    int idxH = 0;
    int idxW = 0;
    for (idxH = 0; idxH < h; idxH++)
    {
        GifRow = Buffer[idxH];
        rgbBuf = inRgb + idxH * w * 3;
        for(idxW = 0; idxW < w; idxW++)
        {
            ColorMapEntry = &ColorMap->Colors[GifRow[idxW]];
      *rgbBuf++ = ColorMapEntry->Blue;
      *rgbBuf++ = ColorMapEntry->Green;
            *rgbBuf++ = ColorMapEntry->Red;    
        }
    }
}
//显示GIF图像
int LCD_DisplayGIF(int x,int y,unsigned char *file)
{
  int error=0;
  int size;
  int i;
  GifRowType *Buffer;
  GifFileType *fp;
    /*1. 打开图片文件*/
  fp=DGifOpenFileName(file,&error);
  if(fp==NULL)return -1;
  printf("GIF图片尺寸:%dx%d\n",fp->SWidth,fp->SHeight);
  /*2. 内存空间申请、初始化*/
  Buffer=(GifRowType*)malloc(fp->SHeight*sizeof(GifRowType));
  /*一行字节大小*/
  size = fp->SWidth*sizeof(GifPixelType);
  Buffer[0]=(GifRowType)malloc(size);
  /*将其颜色设置为BackGround*/
    for(i=0;i<fp->SWidth;i++)
  {
    Buffer[0][i]=fp->SBackGroundColor;
  }
  /*分配其他行,并将它们的颜色也设置为背景 */
  for(i=1;i<fp->SHeight;i++)
  {
    Buffer[i]=(GifRowType)malloc(size);
    memcpy(Buffer[i],Buffer[0],size);
  }
  /*3. 显示图片*/
  ColorMapObject *colorMap=NULL;
  GifByteType *extension=NULL;
  GifRecordType gRecordType=UNDEFINED_RECORD_TYPE;
  int InterlacedOffset[]={0,4,2,1};  // The way Interlaced image should
  int InterlacedJumps[]={8,8,4,2};   // be read - offsets and jumps...
  unsigned char rgbBuf[800 * 480]={0};
  int extCode = 0;
  int row = 0;
  int col = 0;
  int width = 0;
  int height = 0;
  int iW = 0;
  int iH = 0;
  do
  {
    if(DGifGetRecordType(fp,&gRecordType)==GIF_ERROR)break;
    switch(gRecordType)
    {
      case IMAGE_DESC_RECORD_TYPE:
        if(DGifGetImageDesc(fp)==GIF_ERROR)break;
        row=fp->Image.Top;
        col=fp->Image.Left;
        width=fp->Image.Width;
        height=fp->Image.Height;
        if(fp->Image.Interlace)
        {
          for(iH=0;iH<4;iH++)
          {
            for(iW=row+InterlacedOffset[iH];iW<row+height;iW+=InterlacedJumps[iH])
            {
              DGifGetLine(fp,&Buffer[iW][col],width);
            }
          }
        }
        else
        {
          for(iH=0;iH<height;iH++)
          {
            DGifGetLine(fp,&Buffer[row++][col],width);
          }
        }
        colorMap=(fp->Image.ColorMap?fp->Image.ColorMap:fp->SColorMap);
        if(colorMap==NULL)
        {
          break;
        }
        GifBufferToRgb888(colorMap,rgbBuf,Buffer,fp->SWidth,fp->SHeight);
        //将图像显示在LCD屏上
        FrameBufferDraw(x,y,fp->SWidth,fp->SHeight,rgbBuf);
        //帧间隔时间
        usleep(1000*50);
        break;
      case EXTENSION_RECORD_TYPE:
        /* 跳过文件中的所有扩展块*/
        if(DGifGetExtension(fp,&extCode,&extension)==GIF_ERROR)break;
        while(extension!=NULL)
        {
          if(DGifGetExtensionNext(fp, &extension) == GIF_ERROR)break;
        }
        break;
      case TERMINATE_RECORD_TYPE:
        break;
      default:
        break;
    }
  }while(gRecordType!=TERMINATE_RECORD_TYPE);
  /*4. 释放空间*/
  for(i =0;i<fp->SHeight;i++)
    {
       free(Buffer[i]);
    }
    free(Buffer);
    DGifCloseFile(fp,&error);
    return 0;
}
int main(int argc,char **argv)
{
  if(argc!=2)
  {
    printf("./app <GIF图片文件>\n");
    return 0;
  }
  int fd=open("/dev/fb0",O_RDWR);
  if(fd<0)
  {
    perror("设备文件打开失败");
    return 0;
  }
  /*1. 获取LCD屏的可变形参*/
  ioctl(fd,FBIOGET_VSCREENINFO,&var);
  printf("分辨率:%d*%d\n",var.xres,var.yres);
  printf("像素点位数:%d\n",var.bits_per_pixel);
  /*2. 获取LCD屏的固定形参*/
  ioctl(fd,FBIOGET_FSCREENINFO,&fix);
  printf("映射的长度:%d\n",fix.smem_len);
  printf("一行的字节数:%d\n",fix.line_length);
  /*3. 映射LCD缓冲区地址到进程空间*/
  fb_mem=mmap(NULL,fix.smem_len,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
  if(fb_mem==NULL)
  {
    perror("空间映射失败!\n");
    return 0;
  }
  /*4. 控制显示屏*/
  memset(fb_mem,0xFFFFFF,fix.smem_len); //将屏幕清屏为白色
  while(1)
  {
    printf("GIF图片显示状态:%d\n",LCD_DisplayGIF(100,100,argv[1]));
  }
  munmap(fb_mem,fix.smem_len);
  close(fd);
  return 0;
}

五、编译Makefile代码

all:
  arm-linux-gcc lcd_app.c -o app -lgif
  cp app /home/wbyq/project
  rm app -f

六、运行效果图

image.png

image.png

目录
相关文章
|
3月前
|
Shell Linux
Linux shell编程学习笔记30:打造彩色的选项菜单
Linux shell编程学习笔记30:打造彩色的选项菜单
|
11天前
|
存储 监控 Linux
嵌入式Linux系统编程 — 5.3 times、clock函数获取进程时间
在嵌入式Linux系统编程中,`times`和 `clock`函数是获取进程时间的两个重要工具。`times`函数提供了更详细的进程和子进程时间信息,而 `clock`函数则提供了更简单的处理器时间获取方法。根据具体需求选择合适的函数,可以更有效地进行性能分析和资源管理。通过本文的介绍,希望能帮助您更好地理解和使用这两个函数,提高嵌入式系统编程的效率和效果。
67 13
|
1月前
|
缓存 Linux 开发者
Linux内核中的并发控制机制:深入理解与应用####
【10月更文挑战第21天】 本文旨在为读者提供一个全面的指南,探讨Linux操作系统中用于实现多线程和进程间同步的关键技术——并发控制机制。通过剖析互斥锁、自旋锁、读写锁等核心概念及其在实际场景中的应用,本文将帮助开发者更好地理解和运用这些工具来构建高效且稳定的应用程序。 ####
46 5
|
1月前
|
运维 监控 Shell
深入理解Linux系统下的Shell脚本编程
【10月更文挑战第24天】本文将深入浅出地介绍Linux系统中Shell脚本的基础知识和实用技巧,帮助读者从零开始学习编写Shell脚本。通过本文的学习,你将能够掌握Shell脚本的基本语法、变量使用、流程控制以及函数定义等核心概念,并学会如何将这些知识应用于实际问题解决中。文章还将展示几个实用的Shell脚本例子,以加深对知识点的理解和应用。无论你是运维人员还是软件开发者,这篇文章都将为你提供强大的Linux自动化工具。
|
1月前
|
存储 安全 关系型数据库
Linux系统在服务器领域的应用与优势###
本文深入探讨了Linux操作系统在服务器领域的广泛应用及其显著优势。通过分析其开源性、安全性、稳定性和高效性,揭示了为何Linux成为众多企业和开发者的首选服务器操作系统。文章还列举了Linux在服务器管理、性能优化和社区支持等方面的具体优势,为读者提供了全面而深入的理解。 ###
|
3月前
|
Shell Linux
Linux shell编程学习笔记82:w命令——一览无余
Linux shell编程学习笔记82:w命令——一览无余
|
3月前
|
Linux Shell
Linux系统编程:掌握popen函数的使用
记得在使用完 `popen`打开的流后,总是使用 `pclose`来正确关闭它,并回收资源。这种做法符合良好的编程习惯,有助于保持程序的健壮性和稳定性。
160 6
|
3月前
|
Linux Shell
Linux系统编程:掌握popen函数的使用
记得在使用完 `popen`打开的流后,总是使用 `pclose`来正确关闭它,并回收资源。这种做法符合良好的编程习惯,有助于保持程序的健壮性和稳定性。
181 3
|
4月前
|
项目管理 敏捷开发 开发框架
敏捷与瀑布的对决:解析Xamarin项目管理中如何运用敏捷方法提升开发效率并应对市场变化
【8月更文挑战第31天】在数字化时代,项目管理对软件开发至关重要,尤其是在跨平台框架 Xamarin 中。本文《Xamarin 项目管理:敏捷方法的应用》通过对比传统瀑布方法与敏捷方法,揭示敏捷在 Xamarin 项目中的优势。瀑布方法按线性顺序推进,适用于需求固定的小型项目;而敏捷方法如 Scrum 则强调迭代和增量开发,更适合需求多变、竞争激烈的环境。通过详细分析两种方法在 Xamarin 项目中的实际应用,本文展示了敏捷方法如何提高灵活性、适应性和开发效率,使其成为 Xamarin 项目成功的利器。
55 1
|
3月前
|
Shell Linux Python
python执行linux系统命令的几种方法(python3经典编程案例)
文章介绍了多种使用Python执行Linux系统命令的方法,包括使用os模块的不同函数以及subprocess模块来调用shell命令并处理其输出。
89 0