技术交流QQ群【JAVA,C++,Python,.NET,BigData,AI】:170933152
先看这个BMP,可以用画图画一个BMP的图片,然后保存,保存的时候可以选择BMP的格式
保存以后可以打开看一看
去看一下,还可以给这个图片,画上一些颜色等.
然后,BMP图片可以看到有个色彩的深度设置,也就是说可以用1,4,8,16,24,32bit位,去描述一个点的颜色,当然
位越多的话,颜色越清晰,但是占用的空间也越大.
可以看到,刚才画的这个
文件的大小:
300多kb
然后把这个文件保存成jpg看看
可以看到大小一下子就变成了只有48kb,比原来小多了
然后再看看BMP的具体信息
然后再看一下JPG
/JPEG
把一张图片保存成JPG以后,他是有损压缩的,所以肯定会不如BMP图片显得清楚.,但是根本就看不出来,
因为jpg再压缩的过程中也保证了图片质量.
再看一下gif,是无损压缩.
这个gif是把多张图片切换显示做成的图片.
里面是定义了一些规则数据的,比如每张图片播放多长时间等.
接下来主要看实验代码:
打开图片显示实验,这个代码:
可以看到这部分就是操作图片的.c文件
然后再hardware文件夹中可以看到操作SPI FLASH的spi.c,操作sd卡的(后面补上)sdio_sdcard.c
操作lcd的lcd.c,这些不是这次的重点.
这里PICTURE这个文件夹中,
bmp.c用来解析bmp的
gif.c用来解析gif的
piclib.c是用来解析的入口文件
fjpgd.c用来解析jpg的
可以看到这里:
//智能画图
//FileName:要显示的图片文件 BMP/JPG/JPEG/GIF
//x,y,width,height:坐标及显示区域尺寸
//fast:使能jpeg/jpg小图片(图片尺寸小于等于液晶分辨率)快速解码,0,不使能;1,使能.
//图片在开始和结束的坐标点范围内显示
u8 ai_load_picfile(const u8 *filename,u16 x,u16 y,u16 width,u16 height,u8 fast)
{
u8 res;//返回值
u8 temp;
if((x+width)>picinfo.lcdwidth)return PIC_WINDOW_ERR; //x坐标超范围了.
if((y+height)>picinfo.lcdheight)return PIC_WINDOW_ERR; //y坐标超范围了.
//得到显示方框大小
if(width==0||height==0)return PIC_WINDOW_ERR; //窗口设定错误
picinfo.S_Height=height;
picinfo.S_Width=width;
//显示区域无效
if(picinfo.S_Height==0||picinfo.S_Width==0)
{
picinfo.S_Height=lcddev.height;
picinfo.S_Width=lcddev.width;
return FALSE;
}
if(pic_phy.fillcolor==NULL)fast=0;//颜色填充函数未实现,不能快速显示
//显示的开始坐标点
picinfo.S_YOFF=y;
picinfo.S_XOFF=x;
//文件名传递
temp=f_typetell((u8*)filename); //得到文件的类型
switch(temp)
{
case T_BMP:
res=stdbmp_decode(filename); //解码bmp
break;
case T_JPG:
case T_JPEG:
res=jpg_decode(filename,fast); //解码JPG/JPEG
break;
case T_GIF:
res=gif_decode(filename,x,y,width,height); //解码gif
break;
default:
res=PIC_FORMAT_ERR; //非图片格式!!!
break;
}
return res;
}
这个函数,传入文件的路径,然后传入要在什么坐标开始输出这个图片,然后传入图片的宽度,传入图片的高度
然后他就会给显示图片;
这里可以看到,
switch(temp)
{
case T_BMP:
res=stdbmp_decode(filename); //解码bmp,这个是在bmp.c文件中
break;
case T_JPG:
case T_JPEG:
res=jpg_decode(filename,fast); //解码JPG/JPEG,这个是在tjpgd.c文件中
break;
case T_GIF:
res=gif_decode(filename,x,y,width,height); //解码gif,这个是在gif.c文件中
break;
default:
res=PIC_FORMAT_ERR; //非图片格式!!!
break;
}
具体的解码都在各自的.c文件中
这个程序的思路是这样的:
下面是三个图片文件的解码.c文件,然后,再piclib这个.c文件中,根据图片的类型,去分别调用对应的
图片解码文件,然后再piclib中还有一些共用的文件,可以被gif.c等,解码文件去调用,然后
在piclib.c中还有一个操作lcd的函数,用来在lcd屏幕上,显示图片文件.会调用到lcd.c来显示.
看看piclib.h文件
这是函数的声明部分
然后还有两个结构体,这两个结构体很重要,
然后:
第一个结构体,可以看到这里phy结尾的,这个就是物理层的意思.
可以看到这里面,通过把函数指针作为结构体的成员变量.
然后接下来有个变量,带有extern这个关键字,说明这个pic_phy是定义在外面的
这个pic_phy的类型是,_pic_phy
可以看到这个变量是定义在piclib.c文件中的
那么这个变量定义在这里有什么用呢
实际上.可以看到下面有个piclib_init函数
可以看到在这里初始化了.
可以看到这三个函数,
//
//画图初始化,在画图之前,必须先调用此函数
//指定画点/读点
void piclib_init(void)
{
//1.这个可以在LCD.C的文件中找到LCD_ReadPoint
pic_phy.read_point=LCD_ReadPoint; //读点函数实现,仅BMP需要
//2.这个可以在LCD.C的文件中找到draw_point
pic_phy.draw_point=LCD_Fast_DrawPoint; //画点函数实现
//3.LCD_Fill 这个可以在LCD.C的文件中找到LCD_Fill
pic_phy.fill=LCD_Fill; //填充函数实现,仅GIF需要
}
另外还有两个函数
//
//画图初始化,在画图之前,必须先调用此函数
//指定画点/读点
void piclib_init(void)
{
pic_phy.read_point=LCD_ReadPoint; //读点函数实现,仅BMP需要
pic_phy.draw_point=LCD_Fast_DrawPoint; //画点函数实现
pic_phy.fill=LCD_Fill; //填充函数实现,仅GIF需要
//1.这两个函数可以在piclib.c这个文件中找到 piclib_draw_hline
//piclib_fill_color
pic_phy.draw_hline=piclib_draw_hline; //画线函数实现,仅GIF需要
pic_phy.fillcolor=piclib_fill_color; //颜色填充函数实现,仅TJPGD需要
}
然后看看,可以看到上面有定义的,这两个函数
_pic_info picinfo; //图片信息
_pic_phy pic_phy; //图片显示物理接口
//
//lcd.h没有提供划横线函数,需要自己实现
void piclib_draw_hline(u16 x0,u16 y0,u16 len,u16 color)
{
if((len==0)||(x0>lcddev.width)||(y0>lcddev.height))return;
LCD_Fill(x0,y0,x0+len-1,y0,color);
}
//填充颜色
//x,y:起始坐标
//width,height:宽度和高度。
//*color:颜色数组
void piclib_fill_color(u16 x,u16 y,u16 width,u16 height,u16 *color)
{
LCD_Color_Fill(x,y,x+width-1,y+height-1,color);
}
这两个函数,再LCD.C中没有所以这里自己再piclib.c中实现了.
可以看到,这里这样做的好处就是,如果你想要写自己的LCD显示程序,这里,你只需要把
对应的这5个函数,替换成自己的就可以了.
然后看picinfo这个结构体,可以看到他也是在piclib.h文件中定义的
这个主要是图像的信息.
这个是结构体,放了图像的信息
然后,这个结构体,再piclib_init中进行了初始化.
所以在解码图片之前,咱们需要调用这个函数初始化.
然后还要:
picinfo.lcdwidth=lcddev.width; //得到LCD的宽度像素
picinfo.lcdheight=lcddev.height;//得到LCD的高度像素
在初始化的时候,还需要获取LCD 的宽度,和高度像素,实现对这个图片的初始化
然后还有三个函数
//快速ALPHA BLENDING算法.
//src:源颜色
//dst:目标颜色
//alpha:透明程度(0~32)
//返回值:混合后的颜色.
u16 piclib_alpha_blend(u16 src,u16 dst,u8 alpha)
{
u32 src2;
u32 dst2;
//Convert to 32bit |-----GGGGGG-----RRRRR------BBBBB|
src2=((src<<16)|src)&0x07E0F81F;
dst2=((dst<<16)|dst)&0x07E0F81F;
//Perform blending R:G:B with alpha in range 0..32
//Note that the reason that alpha may not exceed 32 is that there are only
//5bits of space between each R:G:B value, any higher value will overflow
//into the next component and deliver ugly result.
dst2=((((dst2-src2)*alpha)>>5)+src2)&0x07E0F81F;
return (dst2>>16)|dst2;
}
//初始化智能画点
//内部调用
void ai_draw_init(void)
{
float temp,temp1;
temp=(float)picinfo.S_Width/picinfo.ImgWidth;
temp1=(float)picinfo.S_Height/picinfo.ImgHeight;
if(temp<temp1)temp1=temp;//取较小的那个
if(temp1>1)temp1=1;
//使图片处于所给区域的中间
picinfo.S_XOFF+=(picinfo.S_Width-temp1*picinfo.ImgWidth)/2;
picinfo.S_YOFF+=(picinfo.S_Height-temp1*picinfo.ImgHeight)/2;
temp1*=8192;//扩大8192倍
picinfo.Div_Fac=temp1;
picinfo.staticx=0xffff;
picinfo.staticy=0xffff;//放到一个不可能的值上面
}
//判断这个像素是否可以显示
//(x,y) :像素原始坐标
//chg :功能变量.
//返回值:0,不需要显示.1,需要显示
u8 is_element_ok(u16 x,u16 y,u8 chg)
{
if(x!=picinfo.staticx||y!=picinfo.staticy)
{
if(chg==1)
{
picinfo.staticx=x;
picinfo.staticy=y;
}
return 1;
}else return 0;
}
这三个函数
这个就是上面说的piclib中定义的,共用的那三个函数
这个函数是一个透明处理的函数.
然后
在屏幕画点的时候的初始化函数
下面这个函数的作用是,如果一个图片,大于LCD屏幕的宽度了,那么
需要把这个图片缩小,这个时候就要判断,这个像素在屏幕上能不能显示.
然后前面那些工作做好以后,就可以去显示图片了
这个函数,就是在指定的位置,去显示一个图片.
可以看到这个函数,前面去做了一些分析,
然后做完前期工作以后,可以看到去调用了这个
f_typetell这个函数
这个函数是确定文件的类型.是bmp还是gif还是别的什么
然后,每个文件中都有对应的解码对应类型图片的代码
因为这里还用到了内存分配和释放,这里就用到了下面的这两个函数.
然后这里pic_get_tnum这个函数用来,获取某个目录中的所有的图片文件的个数.
这个网站,有文件处理的代码,可以免费用.
这里看一下这个函数
这个函数,点进去可以看到,里面有两个参数,一个是dir,这个是传入的文件目录,
然后第二个是FILEINFO 可以看到是OUT,这个是,返回的一个文件的信息.
也就是如果一个文件夹中有多个文件,那么每调用一次这个函数,就返回一个这个文件夹中的文件信息.
然后看看例子代码:
可以看到这里的这个例子,有个scan_files这个函数,
可以看到这个函数先去打开目录,然后再去调用f_readdir读取,这个目录中的文件
然后如果再碰到文件夹,就再去调用自己scan_files这个函数.
这样就返回了所有的文件.
再回去看咱们的pic_get_tnum这个函数.
可以看到咱们的这个函数,也是这样的,也就是说,
去调用f_readdir这个函数,去读取文件,然后,循环的去调用这个函数,然后不停的去获取文件,如果
文件是图片文件,那么就rval就++也就是返回图片的个数.
再去看main.c这个文件
可以看到,这里面
前面是一堆的初始化,然后主要看后面的内容
这里首先要打开这个目录,然后
去这个目录中获取所有的文件数
然后
然后这里,再去记录索引
也就是说,首先打开目录,然后去查找图片文件,然后就把
所有有效的图片文件的索引都记录下来.
接下来,就根据索引开始,然后打开每个文件,去显示了.
然后这里说一个重要的函数,
dir_sdi这个函数.
这个函数是在FATFS这个文件系统中的
然后这个函数的作用是改变索引.
改变当前目录的索引.
也就是,当获取了一个文件以后,调用一下这个函数,他就会把索引指向下一个文件.
然后可以看到去读取文件,然后再去调用ai_load_picfile
去进行显示这个图片.
然后其实也可以,去
去文件夹中,去挨个的去扫描文件然后再显示也行啊
但是我们这样记录下来文件的索引的好处是,下面上一张图片,下一张图片显示的时候
就方便了,可以根据索引拿出来直接显示,而不用再去挨个去文件夹中扫描了.
接下来把代码,下载到开发版
这里,可以看到图片显示的比较慢,
这个是跟,1.处理器性能有关
2.因为这里是从SD卡中读取的
那么如果想快速一点话
可以把图片放在SPI Flash中去,然后还可以放到SRAM中去,
这样速度就会快很多了,这样的话就需要去先把图片,用取模软件,得到图片的
2进制数据,然后再去显示这个图片就可以了.
还可以,先把图片提前进行解码,然后显示的时候就直接显示,也需要提前取模,取到图片的2进制数据.
然后再显示.