Linux小项目-数码相册设计

Posted DS小龙哥

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Linux小项目-数码相册设计相关的知识,希望对你有一定的参考价值。

1. 前言

这是基于Linux系统开发板设计一个小项目-数码相册,在LCD屏上可以显示完成常见的图片显示,翻页、旋转、缩放等功能。

开发板采用友善之臂的Tiny4412开发板,CPU是三星的4412,最高主频1.5GHZ。板子配有8G的EMMC,2G的DDR,运行Linux3.5内核,文件系统采用busybox制作的最小根文件系统,不带图形桌面框架,系统是最小最精简的系统。

要完成整个项目相册的功能,需要的东西还是比较多的,首先要编译安装各种图片库: libjpg,giflib,libpng等等,图片需要缩放,需要支持缩放算法;LCD界面上的文字,时间采用矢量字体显示的,还需要交叉编译安装freetype库。然后硬件层,需要编写LCD屏驱动(帧缓冲框架),触摸屏驱动(输入子系统),三轴加速度计驱动(mma7660飞思卡尔的芯片)。

整个项目的代码布局如下:

如果把整个项目代码写完一遍,基本上Linux驱动、应用层编程都能够熟悉一遍。

涉及的技术点总结:

(1)png、jpg、gif等各种开源图片库的编译安装,完成对应图片解码,显示。整个过程里还需要懂得png、jpg、GIF图片的构造原理,如何读取数据,如何提取rgb数据,最终在LCD屏上完成显示。

(2)LCD驱动编写,首先得了解Linux帧缓冲框架原理,明白LCD屏的时序,才能编写驱动。应用层需要明白如何针对帧缓冲框架完成应用编程,实现画点、画线、文字等基本显示。

(3)触摸屏驱编写,触摸屏驱动芯片是FT5X06,这个IIC接口的芯片,编写触摸屏驱动需要熟悉IIC子系统、输入子系统、内核中断、工作队列等框架,因为触摸屏芯片支持笔中断,需要注册中断,在中断服务函数里调用工作队列实时读取数据。 应用层还需要适配tslib库接口,让tslib去读取输入子系统上传的坐标,还能完成校准,测试等功能,最终在自己工程内再封装函数去读取tslib接口返回的坐标数据,完成触屏屏逻辑处理。

(4)三轴加速度mma7660驱动编写,通过三轴加速度测量开发板的姿态,完成数码相册里图片的自动上下左右翻转,手机相册都有这个功能,横竖屏切换。

(5)按键驱动编写,数码相册支持按键翻页、触摸屏滑动翻页、自动图片播放(幻灯片)等功能,所以还需要编写按键驱动,按键驱动采用杂项字符设备编写,通过ioctl接口上传按键值。

(6)矢量字体库编译安装,界面上需要显示各种文字提示、时间等信息。用到矢量字体ttc,ttf等。

图片的翻页采用双向链表完成,支持左右翻页,更新链表时,将指定目录下所有图片加到双向链表里,通过按键、触摸屏、自动播放时,从链表里获取图片地址完成显示。

2. 涉及到源代码

项目Get: https://download.csdn.net/download/xiaolong1126626497/85248626

2.1 png图片显示

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <termios.h>
#include <unistd.h>
#include <string.h>
#include <linux/fb.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <linux/videodev2.h>
#include <poll.h>
#include <png.h>
#include <pngconf.h>

#define LCD_DEVICE "/dev/fb0"

int lcd_fd;
struct fb_var_screeninfo vinfo;//可变参数
struct fb_fix_screeninfo finfo; //固定参数
unsigned char *lcd_mem=NULL; //LCD首地址
typedef unsigned int u32;
typedef unsigned short u16;
typedef unsigned char u8;

int image_height;
int image_width;
unsigned char *image_buffer[4];
int video_fd;
void LCD_DrawPoint(u32 x,u32 y,u32 c);
u32 LCD_ReadPoint(u32 x,u32 y);


/*显示PNG文件*/
int display_png(u32 x,u32 y,char* filename) 

	FILE *fp;
	png_structp png_ptr;
	png_infop info_ptr;
	png_uint_32 width, height;
	int bit_depth, color_type, interlace_type, number_passes;
	u32 i,j;
	u32 x0;
	u32 rgb24,b_rgb24;
	u8 r,g,b,a;
	u8 b_r,b_g,b_b;
	u8 R,G,B;
	
    if((fp = fopen(filename,"rb")) == NULL)
    
      printf("%s 文件打开失败.\\n",filename);
      return -1;
    
    png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,NULL, NULL, NULL);
    /*需要分配/初始化内存以获取图像信息*/
	info_ptr = png_create_info_struct(png_ptr);
	/*设置PNG图片的文件指针*/
    png_init_io(png_ptr,fp);
    png_read_info(png_ptr,info_ptr);
    png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type,&interlace_type, NULL, NULL);  
	printf("图片宽度:[%4d]\\n",width);
	printf("图片高度:[%4d]\\n",height);
	printf("颜色位数:[%4d]\\n",bit_depth); //ARGB
	
    /*读取图像的最简单方法:*/
    png_bytep row_pointers[height];

	/*清除指针数组*/
	for(i = 0; i < height; i++)
	
		row_pointers[i] = NULL;
		row_pointers[i] = malloc(width * 4); /* RGBA */
		memset(row_pointers[i], 0, width * 4);
	
	/*读取整个PNG图像*/
	png_read_image(png_ptr,row_pointers);

    for(i = 0; i < height; i++)
    
		x0=x;
		for(j = 0; j < width * 4; j += 4)
		
			/*得到图片颜色*/
			r=row_pointers[i][j + 0];
			g=row_pointers[i][j + 1];
			b=row_pointers[i][j + 2];
			a=row_pointers[i][j + 3];
			
			/*读取当前屏幕点的背景颜色*/
			b_rgb24=LCD_ReadPoint(x0,y);
			b_r=b_rgb24>>16&0xFF;
			b_g=b_rgb24>>8&0xFF;
			b_b=b_rgb24>>0&0xFF;
			
			/*合成屏幕背景颜色*/
			R = (unsigned char)(r * (a / 255.0) + (b_r * (255 - a)) / 255.0);
			G = (unsigned char)(g * (a / 255.0) + (b_g * (255 - a)) / 255.0);
			B = (unsigned char)(b * (a / 255.0) + (b_b * (255 - a)) / 255.0);
		
			/*显示数据*/
			rgb24=R<<16|G<<8|B;
			LCD_DrawPoint(x0,y,rgb24);
			
			/*坐标自增*/
			x0++;
		
		y++;
	
    /* 读取文件的其余部分,并在info_ptr中获取其他块-必需*/
    png_read_end(png_ptr, info_ptr);
   /*读取后清理,并释放已分配的所有内存-必需*/
    png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
    /* 统一释放内存 */
	for(i = 0; i < height; i++)
	
		free(row_pointers[i]);
	
    /*关闭文件*/
    fclose(fp);
    return 0;



/*
函数功能: 封装画点函数
函数参数: u32 x,u32 y,u16 c
*/
void LCD_DrawPoint(u32 x,u32 y,u32 c)

	 u32 *lcd_p=(u32*)(lcd_mem+vinfo.xres*vinfo.bits_per_pixel/8*y+x*vinfo.bits_per_pixel/8);
	 *lcd_p=c;



/*
函数功能: 封装读点函数
函数参数: u32 x,u32 y,u16 c
*/
u32 LCD_ReadPoint(u32 x,u32 y)

	 u32 *lcd_p=(u32*)(lcd_mem+vinfo.xres*vinfo.bits_per_pixel/8*y+x*vinfo.bits_per_pixel/8);
	 return *lcd_p;


int main(int argc,char **argv)

	int err;
	if(argc!=2)
	
		printf("./app <xxx.png>\\n");
		return 0;
	
	
	/*1. 打开设备文件*/
	lcd_fd=open(LCD_DEVICE,O_RDWR);
	if(lcd_fd<0)
	
		printf("%s 设备文件打开失败.\\n",LCD_DEVICE);
		return 0;
	
	/*2. 获取可变参数*/
	ioctl(lcd_fd,FBIOGET_VSCREENINFO,&vinfo);
	printf("x=%d,y=%d,pixel=%d\\n",vinfo.xres,vinfo.yres,vinfo.bits_per_pixel);
	
	/*3. 获取固定参数*/
	ioctl(lcd_fd,FBIOGET_FSCREENINFO,&finfo);
	printf("smem_len=%d\\n",finfo.smem_len);
	printf("line_length=%d\\n",finfo.line_length);

	/*4. 映射LCD地址*/
	lcd_mem=mmap(NULL,finfo.smem_len,PROT_READ|PROT_WRITE,MAP_SHARED,lcd_fd,0);
	if(lcd_mem==NULL)
	
		printf("映射LCD地址失败.\\n");
		return -1;
	
	//memset(lcd_mem,0xFFFFFF,finfo.smem_len);
	
	/*5. 显示PN图片*/
	display_png(0,0,argv[1]);
	
	close(lcd_fd);
	return 0;

2.2 jpg图片显示

#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 <jpeglib.h>
#include <jerror.h>

struct fb_var_screeninfo var;	//可变参数
struct fb_fix_screeninfo fix;	//固定参数
unsigned char *fb_mem=NULL; 	//LCD屏的首地址

/*
函数功能: 画点
*/
void Show_Pixel(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; //颜色赋值



//显示JPEG   jpeglib
int LCD_ShowJPEG(int x,int y,unsigned char *file)

    struct jpeg_decompress_struct cinfo; //存放图像的数据
    struct jpeg_error_mgr jerr; //存放错误信息
    FILE         *infile;
    unsigned int *dst=(unsigned int *)fb_mem;
    unsigned char  *buffer;
	unsigned int i;
	unsigned int  color;
	
    /* 打开图像文件*/
    if ((infile = fopen(file, "rb")) == NULL) 
	
        perror("jpeg文件打开失败!\\n");
        return -1;
    

    /*init jpeg压缩对象错误处理程序*/
    cinfo.err = jpeg_std_error(&jerr); //初始化标准错误,用来存放错误信息
    jpeg_create_decompress(&cinfo);    //创建解压缩结构信息
     
    /*将jpeg压缩对象绑定到infile*/
    jpeg_stdio_src(&cinfo, infile);

    /*读jpeg头*/
    jpeg_read_header(&cinfo, TRUE);
   
    /*开始解压*/
    jpeg_start_decompress(&cinfo);
    
    printf("JPEG图片高度: %d\\n",cinfo.output_height);
    printf("JPEG图片宽度: %d\\n",cinfo.output_width);
    printf("JPEG图片颜色位数(字节单位): %d\\n",cinfo.output_components);
    
    /*为一条扫描线上的像素点分配存储空间,一行一行的解码*/
    buffer = (unsigned char *)malloc(cinfo.output_width *cinfo.output_components);
    
	//将图片内容显示到framebuffer上,cinfo.output_scanline表示当前行的位置,读取数据是会自动增加
    while(cinfo.output_scanline < cinfo.output_height) 
    
         //读取一行的数据    
        jpeg_read_scanlines(&cinfo,&buffer,1);
		for(i = 0; i<cinfo.output_width; i++) 
		
			//取出一个像素点(从左到右)
			color = buffer[i * 3 + 0] << 16  |
					buffer[i * 3 + 1] << 8   |
					buffer[i * 3 + 2] << 0;
			//画点
			Show_Pixel(x+i,y,color);
		
        y++;// 继续下一行
    
    
    /*完成解压,摧毁解压对象*/
    jpeg_finish_decompress(&cinfo); //结束解压
    jpeg_destroy_decompress(&cinfo); //释放结构体占用的空间

    /*释放内存缓冲区*/
    free(buffer);

    /*关闭文件*/
    fclose(infile);
    return 0;



int main(int argc,char **argv)

	if(argc!=2)
	
		printf("./app <JPEG图片文件>\\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); //将屏幕清屏为白色
	
	LCD_ShowJPEG(0,0,argv[1]);
	
	munmap(fb_mem,fix.smem_len);
	close(fd);
	return 0;

2.3 gif图片显示

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

以上是关于Linux小项目-数码相册设计的主要内容,如果未能解决你的问题,请参考以下文章

Linux小项目-数码相册设计

Html制作滑动相册

基于Android的在线相册管理系统

什么电脑桌面插件可以显示电子相册啊?

win10下使用AIDA64建立副屏监控

win10下使用AIDA64建立副屏监控