6

#yyds干货盘点# Linux小项目-数码相册设计

 2 years ago
source link: https://blog.51cto.com/u_11822586/5018745
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

#yyds干货盘点# Linux小项目-数码相册设计

原创

DS小龙哥 2022-02-18 09:03:54 博主文章分类:STM32项目开发 ©著作权

文章标签 #include 触摸屏 数据 文章分类 嵌入式Linux 嵌入式 阅读数1556

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

#yyds干货盘点# Linux小项目-数码相册设计_数据

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

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

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

#yyds干货盘点# Linux小项目-数码相册设计_触摸屏_02

如果把整个项目代码写完一遍,基本上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. 涉及到源代码

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

2.4 矢量字体调用显示

#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 <math.h>
#include <wchar.h>

#include <ft2build.h>
#include FT_FREETYPE_H
#include FT_STROKER_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;

/*定义一个结构体存放矢量字体的配置*/
struct FREE_TYPE_CONFIG
{
	FT_Library    library;
	FT_Face       face;
	FT_GlyphSlot  slot;
	FT_Vector     pen;                    /* untransformed origin  */
	FT_Error      error;
	FT_BBox  bbox;
	FT_Glyph  glyph;
};
struct FREE_TYPE_CONFIG FreeTypeConfig;



/*
函数功能: 封装画点函数
函数参数: 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;
}


/*	LCD显示矢量字体的位图信息
 *		bitmap : 要显示的字体的矢量位图
 *		x : 显示的x坐标
 *		y : 显示的y坐标
 */
void LCD_DrawBitmap(FT_Bitmap* bitmap,FT_Int x,FT_Int y)
{
  	FT_Int i,j,p,q;
  	FT_Int x_max=x+bitmap->width;
  	FT_Int y_max=y+bitmap->rows;

	/* 将位图信息循环打印到屏幕上 */
	for(i=x,p=0;i<x_max;i++,p++)
	{
		for(j=y,q=0;j<y_max;j++,q++)
		{
			if((i>x_max)||(j>y_max)||(i<0)||(j<0))continue;
			if(bitmap->buffer[q*bitmap->width+p]!=0)
			{
				LCD_DrawPoint(i, j,0xFF0033);
			}
			else
			{
				LCD_DrawPoint(i, j,0xFFFFFF);
			}
		}
	}
}

/*
函数功能: 初始化FreeType配置
*/
int InitConfig_FreeType(char *font_file)
{
	FT_Error      error;
	/*1. 初始化freetype库*/
	error=FT_Init_FreeType(&FreeTypeConfig.library);
	if(error)
	{
		printf("freetype字体库初始化失败.\n");
		return -1;
	}

	/*2. 打开加载的字体文件*/
 	error=FT_New_Face(FreeTypeConfig.library,font_file,0,&FreeTypeConfig.face);
  	if(error)
  	{
		printf("矢量字体文件加载失败.\n");
		return -2;
	}
	return 0;
}

/*
函数功能: 释放FreeType配置
*/
void FreeType_Config(void)
{
	FT_Done_Face(FreeTypeConfig.face);
  	FT_Done_FreeType(FreeTypeConfig.library);
}

/*
函数功能: 在LCD屏显示一串文本数据
函数参数:
	u32 x   坐标位置
	u32 y   坐标位置
	u32 size 字体大小
	wchar_t *text 显示的文本数据
*/
int LCD_DrawText(u32 x,u32 y,u32 size,wchar_t *text)
{
	FT_Error      error;
	int i = 0;
	int bbox_height_min = 10000;
	int bbox_height_max = 0;
		
	/*3. 设置字符的像素的大小为size*size*/
	error=FT_Set_Pixel_Sizes(FreeTypeConfig.face,size,0);
	if(error)
	{
		printf("字符的像素大小设置失败.\n");
		return -1;
	}
	
	/*4. 设置字体文件的轮廓的插槽*/
	FreeTypeConfig.slot=FreeTypeConfig.face->glyph;

	/* 设置坐标为原点坐标
	 * 将LCD坐标转换成笛卡尔坐标
	 * 单位是 1/64 Point
	 */
	FreeTypeConfig.pen.x=x*64;
  	FreeTypeConfig.pen.y=(vinfo.yres-size-y)*64;

	/*5. 循环的将文字显示出来*/
	for(i=0;i<wcslen(text);i++)
	{
		FT_Set_Transform(FreeTypeConfig.face,0,&FreeTypeConfig.pen);	//设置字体的起始坐标位置
		/*装载字符编码,填充face的glyph slot成员*/
		error=FT_Load_Char(FreeTypeConfig.face,text[i],FT_LOAD_RENDER);
		if(error)
		{
			printf("装载字符编码失败.\n");
			return -1;
		}
		
		/*通过glyph slot来获得glyph*/
		FT_Get_Glyph(FreeTypeConfig.slot,&FreeTypeConfig.glyph);

		/*通过glyph来获得cbox*/
		FT_Glyph_Get_CBox(FreeTypeConfig.glyph,FT_GLYPH_BBOX_TRUNCATE,&FreeTypeConfig.bbox);

		/*获得字体高度的最大值和最小值*/
		if(bbox_height_min>FreeTypeConfig.bbox.yMin)bbox_height_min=FreeTypeConfig.bbox.yMin;
		if(bbox_height_max<FreeTypeConfig.bbox.yMax)bbox_height_max=FreeTypeConfig.bbox.yMax;
		
		/*画点,把笛卡尔坐标转换成LCD坐标*/
		LCD_DrawBitmap(&FreeTypeConfig.slot->bitmap,
						FreeTypeConfig.slot->bitmap_left,
						vinfo.yres-FreeTypeConfig.slot->bitmap_top);

		if(FreeTypeConfig.slot->bitmap_left+size*2>vinfo.xres)
		{
			FreeTypeConfig.pen.x=0; //更新X坐标位置
			FreeTypeConfig.pen.y=(vinfo.yres-size-y-size)*64; //更新Y坐标位置
		}
		else
		{
			/* 更新原点坐标位置 */
			FreeTypeConfig.pen.x+=FreeTypeConfig.slot->advance.x;
			FreeTypeConfig.pen.y+=FreeTypeConfig.slot->advance.y;
		}
	}
	return 0;
}

int main(int argc,char **argv)
{	
	if(argc!=2)
	{
		printf("./app <xxx.ttf 字体文件>\n");
		return 0;
	}
	
	/*1. 打开设备文件*/
	lcd_fd=open(LCD_DEVICE,O_RDWR);
	if(lcd_fd<0)
	{
		printf("%s open error.\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. 初始化配置FreeType*/
	InitConfig_FreeType(argv[1]);
	
	/*6. 在指定位置显示文本*/
	/*
	wcslen() 函数用于计算宽字符的个数,支持区分中文和英文字符,文本需要在UTF-8编码下。
	定义宽字符串示例:
	wchar_t *wp=L"1234567890中国"; //12
	printf("wcslen p:%d\n",wcslen(wp)); 返回值是12
	*/
	LCD_DrawText(50,56*0,56,L"Linux项目开发");
	LCD_DrawText(150,56*1,56,L"数码相册开发");
	LCD_DrawText(200,56*3,48,L"FreeType矢量字体");
	LCD_DrawText(150,56*5,80,L"Linux驱动开发");
	/*7. 释放FreeType配置*/
	FreeType_Config();
	
	close(lcd_fd);
	return 0;
}

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK