Files
player/Project/Src/JPEG/bmp.c

532 lines
16 KiB
C
Raw Normal View History

2025-06-27 00:32:57 +08:00
#include "base.h"
#include "bmp.h"
#include "string.h"
#include "ff.h"
#include "mymem.h"
//////////////////////////////////////////////////////////////////////////////////
//本程序只供学习使用,未经作者许可,不得用于其它任何用途
//ALIENTEK STM32F407开发板
//图片解码 驱动代码-bmp解码部分
//正点原子@ALIENTEK
//技术论坛:www.openedv.com
//创建日期:2014/5/15
//版本V1.0
//版权所有,盗版必究。
//Copyright(C) 广州市星翼电子科技有限公司 2014-2024
//All rights reserved
//********************************************************************************
//升级说明
//V1.1 20140722
//修改minibmp_decode函数,使图片在设定区域的正中央显示
//////////////////////////////////////////////////////////////////////////////////
//不使用内存分配
#if BMP_USE_MALLOC == 0
FIL f_bfile;
u8 bmpreadbuf[BMP_DBUF_SIZE];
#endif
struct _pic_phy
{
void (*draw_hline)(int x,int y,int len,u16 color);
void (*draw_point)(int x,int y,u16 color);
void (*fill)(int sx,int xy,int ex,int ey,u16 color);
};
static BMP_DecodeStruct *g_decode=0;
static void draw_hline (int x,int y,int len,u16 color)
{
if (x>=0&&x<g_decode->x_size)
{
if (x+len>g_decode->x_size) len=g_decode->x_size-x;
int ds=y*g_decode->x_size+x;
for (int i=0;i<len;i++)
(g_decode->data)[ds+i]=color;
}
}
static void draw_point (int x,int y,u16 color)
{
int ds=y*g_decode->x_size+x;
(g_decode->data)[ds]=color;
}
static void fill(int sx,int sy,int ex,int ey,u16 color)
{
for (int i=0;i<ey-sy+1;i++)
{
draw_hline (sx,sy+i,ex-sx+1,color);
}
}
static struct _pic_phy pic_phy ={
.draw_hline=draw_hline,
.draw_point=draw_point,
.fill=fill,
};
//标准的bmp解码,解码filename这个BMP文件
//速度比较慢.主要
//filename:包含路径的文件名
//返回值:0,成功;
// 其他,错误码.
int BMP_Decode(const u8 *filename,BMP_DecodeStruct *bmp)
{
FIL* f_bmp;
UINT br;
u16 count;
u8 rgb ,color_byte;
u16 x ,y,color;
u16 countpix=0;//记录像素
//x,y的实际坐标
u16 realx=0;
u16 realy=0;
u8 yok=1;
int res=0;
u8 *databuf; //数据读取存放地址
u16 readlen=BMP_DBUF_SIZE;//一次从SD卡读取的字节数长度
u8 *bmpbuf; //数据解码地址
u8 biCompression=0; //记录压缩方式
u16 rowlen; //水平方向字节数
BITMAPINFO *pbmp; //临时指针
databuf=(u8*)mymalloc(readlen); //开辟readlen字节的内存区域
if(databuf==NULL)return -1; //内存申请失败.
f_bmp=(FIL *)mymalloc(sizeof(FIL)); //开辟FIL字节的内存区域
if(f_bmp==NULL) //内存申请失败.
{
myfree(databuf);
return -1;
}
res=f_open(f_bmp,(const TCHAR*)filename,FA_READ);//打开文件
if(res==0)//打开成功.
{
f_read(f_bmp,databuf,readlen,(UINT*)&br); //读出readlen个字节
pbmp=(BITMAPINFO*)databuf; //得到BMP的头部信息
count=pbmp->bmfHeader.bfOffBits; //数据偏移,得到数据段的开始地址
color_byte=pbmp->bmiHeader.biBitCount/8; //彩色位 16/24/32
biCompression=pbmp->bmiHeader.biCompression;//压缩方式
g_decode=bmp;
g_decode->y_size =pbmp->bmiHeader.biHeight; //得到图片高度
g_decode->x_size=pbmp->bmiHeader.biWidth; //得到图片宽度
g_decode->data = mymalloc_exm (g_decode->x_size*g_decode->y_size*2);
//ai_draw_init();//初始化智能画图
//水平像素必须是4的倍数!!
if((g_decode->x_size*color_byte)%4)rowlen=((g_decode->x_size*color_byte)/4+1)*4;
else rowlen=g_decode->x_size*color_byte;
//开始解码BMP
color=0;//颜色清空
x=0 ;
y=g_decode->y_size;
rgb=0;
//对于尺寸小于等于设定尺寸的图片,进行快速解码
//realy=(y*picinfo.Div_Fac)>>13;
realy=y;
bmpbuf=databuf;
while(1)
{
while(count<readlen) //读取一簇1024扇区 (SectorsPerClust 每簇扇区数)
{
if(color_byte==3) //24位颜色图
{
switch (rgb)
{
case 0:
color=bmpbuf[count]>>3; //B
break ;
case 1:
color+=((u16)bmpbuf[count]<<3)&0X07E0;//G
break;
case 2 :
color+=((u16)bmpbuf[count]<<8)&0XF800;//R
break ;
}
}else if(color_byte==2) //16位颜色图
{
switch(rgb)
{
case 0 :
if(biCompression==BI_RGB)//RGB:5,5,5
{
color=((u16)bmpbuf[count]&0X1F); //R
color+=(((u16)bmpbuf[count])&0XE0)<<1; //G
}else //RGB:5,6,5
{
color=bmpbuf[count]; //G,B
}
break ;
case 1 :
if(biCompression==BI_RGB)//RGB:5,5,5
{
color+=(u16)bmpbuf[count]<<9; //R,G
}else //RGB:5,6,5
{
color+=(u16)bmpbuf[count]<<8; //R,G
}
break ;
}
}else if(color_byte==4)//32位颜色图
{
switch (rgb)
{
case 0:
color=bmpbuf[count]>>3; //B
break ;
case 1:
color+=((u16)bmpbuf[count]<<3)&0X07E0;//G
break;
case 2 :
color+=((u16)bmpbuf[count]<<8)&0XF800;//R
break ;
case 3 :
//alphabend=bmpbuf[count];//不读取 ALPHA通道
break ;
}
}else if(color_byte==1)//8位色,暂时不支持,需要用到颜色表.
{
}
rgb++;
count++ ;
if(rgb==color_byte) //水平方向读取到1像素数数据后显示
{
if(x<g_decode->x_size)
{
//realx=(x*picinfo.Div_Fac)>>13;//x轴实际值
realx=x;
pic_phy.draw_point(realx,realy-1,color);//显示图片
//POINT_COLOR=color;
//LCD_DrawPoint(realx+picinfo.S_XOFF,realy+picinfo.S_YOFF);
//SRAMLCD.Draw_Point(realx+picinfo.S_XOFF,realy+picinfo.S_YOFF,color);
}
x++;//x轴增加一个像素
color=0x00;
rgb=0;
}
countpix++;//像素累加
if(countpix>=rowlen)//水平方向像素值到了.换行
{
y--;
if(y==0)break;
//realy=(y*picinfo.Div_Fac)>>13;//实际y值改变
realy=y;
//if(is_element_ok(realx,realy,0))yok=1;//此处不改变picinfo.staticx,y的值
//else yok=0;
yok=0;
x=0;
countpix=0;
color=0x00;
rgb=0;
}
}
res=f_read(f_bmp,databuf,readlen,(UINT *)&br);//读出readlen个字节
if(br!=readlen)readlen=br; //最后一批数据
if(res||br==0)break; //读取出错
bmpbuf=databuf;
count=0;
}
f_close(f_bmp);//关闭文件
}
myfree(databuf);
myfree(f_bmp);
return res; //BMP显示结束.
}
//小尺寸的bmp解码,解码filename这个BMP文件
//filename:包含路径的文件名
//x,y,width,height:显示区域大小(在区域正中央显示)
//acolor:附加的alphablend的颜色(这个仅对32位色bmp有效!!!)
//mode:模式(除了bit5,其他的均只对32位色bmp有效!!!)
// bit[7:6]:0,仅使用图片本身和底色alphablend;
// 1,仅图片和acolor进行alphablend,并且不适用附加的透明度;
// 2,底色,acolor,图片,一起进行alphablend;
// bit5:保留
// bit4~0:0~31,使用附加alphablend的透明程度
//返回值:0,成功;
// 其他,错误码.
/*
u8 minibmp_decode(u8 *filename,u16 x,u16 y,u16 width,u16 height,u16 acolor,u8 mode)//尺寸小于240*320的bmp图片解码.
{
FIL* f_bmp;
u16 br;
u8 color_byte;
u16 tx,ty,color;
//tx,ty的实际坐标
u8 res;
u16 i,j;
u8 *databuf; //数据读取存 放地址
u16 readlen=BMP_DBUF_SIZE;//一次从SD卡读取的字节数长度,不能小于LCD宽度*3!!!
u8 *bmpbuf; //数据解码地址
u8 biCompression=0; //记录压缩方式
u16 rowcnt; //一次读取的行数
u16 rowlen; //水平方向字节数
u16 rowpix=0; //水平方向像素数
u8 rowadd; //每行填充字节数
u16 tmp_color;
u8 alphabend=0xff; //代表透明色为0完全不透明
u8 alphamode=mode>>6; //得到模式值,0/1/2
BITMAPINFO *pbmp; //临时指针
//得到窗体尺寸
picinfo.S_Height=height;
picinfo.S_Width=width;
#if BMP_USE_MALLOC == 1 //使用malloc
databuf=(u8*)pic_memalloc(readlen); //开辟readlen字节的内存区域
if(databuf==NULL)return PIC_MEM_ERR; //内存申请失败.
f_bmp=(FIL *)pic_memalloc(sizeof(FIL)); //开辟FIL字节的内存区域
if(f_bmp==NULL) //内存申请失败.
{
pic_memfree(databuf);
return PIC_MEM_ERR;
}
#else
databuf=bmpreadbuf;
f_bmp=&f_bfile;
#endif
res=f_open(f_bmp,(const TCHAR*)filename,FA_READ);//打开文件
if(res==0)//打开成功.
{
f_read(f_bmp,databuf,sizeof(BITMAPINFO),(UINT*)&br);//读出BITMAPINFO信息
pbmp=(BITMAPINFO*)databuf; //得到BMP的头部信息
color_byte=pbmp->bmiHeader.biBitCount/8; //彩色位 16/24/32
biCompression=pbmp->bmiHeader.biCompression;//压缩方式
picinfo.ImgHeight=pbmp->bmiHeader.biHeight; //得到图片高度
picinfo.ImgWidth=pbmp->bmiHeader.biWidth; //得到图片宽度
//水平像素必须是4的倍数!!
if((picinfo.ImgWidth*color_byte)%4)rowlen=((picinfo.ImgWidth*color_byte)/4+1)*4;
else rowlen=picinfo.ImgWidth*color_byte;
rowadd=rowlen-picinfo.ImgWidth*color_byte; //每行填充字节数
//开始解码BMP
color=0;//颜色清空
tx=0 ;
ty=picinfo.ImgHeight-1;
if(picinfo.ImgWidth<=picinfo.S_Width&&picinfo.ImgHeight<=picinfo.S_Height)
{
x+=(picinfo.S_Width-picinfo.ImgWidth)/2; //偏移到正中央
y+=(picinfo.S_Height-picinfo.ImgHeight)/2; //偏移到正中央
rowcnt=readlen/rowlen; //一次读取的行数
readlen=rowcnt*rowlen; //一次读取的字节数
rowpix=picinfo.ImgWidth; //水平像素数就是宽度
f_lseek(f_bmp,pbmp->bmfHeader.bfOffBits); //偏移到数据起始位置
while(1)
{
res=f_read(f_bmp,databuf,readlen,(UINT *)&br); //读出readlen个字节
bmpbuf=databuf; //数据首地址
if(br!=readlen)rowcnt=br/rowlen; //最后剩下的行数
if(color_byte==3) //24位BMP图片
{
for(j=0;j<rowcnt;j++) //每次读到的行数
{
for(i=0;i<rowpix;i++)//写一行像素
{
color=(*bmpbuf++)>>3; //B
color+=((u16)(*bmpbuf++)<<3)&0X07E0; //G
color+=(((u16)*bmpbuf++)<<8)&0XF800; //R
pic_phy.draw_point(x+tx,y+ty,color);//显示图片
tx++;
}
bmpbuf+=rowadd;//跳过填充区
tx=0;
ty--;
}
}else if(color_byte==2)//16位BMP图片
{
for(j=0;j<rowcnt;j++)//每次读到的行数
{
if(biCompression==BI_RGB)//RGB:5,5,5
{
for(i=0;i<rowpix;i++)
{
color=((u16)*bmpbuf&0X1F); //R
color+=(((u16)*bmpbuf++)&0XE0)<<1; //G
color+=((u16)*bmpbuf++)<<9; //R,G
pic_phy.draw_point(x+tx,y+ty,color);//显示图片
tx++;
}
}else //RGB 565
{
for(i=0;i<rowpix;i++)
{
color=*bmpbuf++; //G,B
color+=((u16)*bmpbuf++)<<8; //R,G
pic_phy.draw_point(x+tx,y+ty,color);//显示图片
tx++;
}
}
bmpbuf+=rowadd;//跳过填充区
tx=0;
ty--;
}
}else if(color_byte==4) //32位BMP图片
{
for(j=0;j<rowcnt;j++) //每次读到的行数
{
for(i=0;i<rowpix;i++)
{
color=(*bmpbuf++)>>3; //B
color+=((u16)(*bmpbuf++)<<3)&0X07E0; //G
color+=(((u16)*bmpbuf++)<<8)&0XF800; //R
alphabend=*bmpbuf++; //ALPHA通道
if(alphamode!=1) //需要读取底色
{
tmp_color=pic_phy.read_point(x+tx,y+ty);//读取颜色
if(alphamode==2)//需要附加的alphablend
{
tmp_color=piclib_alpha_blend(tmp_color,acolor,mode&0X1F); //与指定颜色进行blend
}
color=piclib_alpha_blend(tmp_color,color,alphabend/8); //和底色进行alphablend
}else tmp_color=piclib_alpha_blend(acolor,color,alphabend/8); //与指定颜色进行blend
pic_phy.draw_point(x+tx,y+ty,color);//显示图片
tx++;//x轴增加一个像素
}
bmpbuf+=rowadd;//跳过填充区
tx=0;
ty--;
}
}
if(br!=readlen||res)break;
}
}
f_close(f_bmp);//关闭文件
}else res=PIC_SIZE_ERR;//图片尺寸错误
#if BMP_USE_MALLOC == 1 //使用malloc
pic_memfree(databuf);
pic_memfree(f_bmp);
#endif
return res;
}
*/
//BMP编码函数
//将当前LCD屏幕的指定区域截图,存为16位格式的BMP文件 RGB565格式.
//保存为rgb565则需要掩码,需要利用原来的调色板位置增加掩码.这里我们已经增加了掩码.
//保存为rgb555格式则需要颜色转换,耗时间比较久,所以保存为565是最快速的办法.
//filename:存放路径
//x,y:在屏幕上的起始坐标
//mode:模式.0,仅仅创建新文件的方式编码;1,如果之前存在文件,则覆盖之前的文件.如果没有,则创建新的文件.
//返回值:0,成功;其他,错误码.
u8 bmp_encode(u8 *filename,u16 x,u16 y,u16 width,u16 height,u8 mode)
{
// FIL* f_bmp;
// u16 bmpheadsize; //bmp头大小
// BITMAPINFO hbmp; //bmp头
// u8 res=0;
// u16 tx,ty; //图像尺寸
// u16 *databuf; //数据缓存区地址
// u16 pixcnt; //像素计数器
// u16 bi4width; //水平像素字节数
// if(width==0||height==0)return PIC_WINDOW_ERR; //区域错误
// if((x+width-1)>lcddev.width)return PIC_WINDOW_ERR; //区域错误
// if((y+height-1)>lcddev.height)return PIC_WINDOW_ERR; //区域错误
//
//#if BMP_USE_MALLOC == 1 //使用malloc
// databuf=(u16*)pic_memalloc(1024); //开辟至少bi4width大小的字节的内存区域 ,对240宽的屏,480个字节就够了.
// if(databuf==NULL)return PIC_MEM_ERR; //内存申请失败.
// f_bmp=(FIL *)pic_memalloc(sizeof(FIL)); //开辟FIL字节的内存区域
// if(f_bmp==NULL) //内存申请失败.
// {
// pic_memfree(databuf);
// return PIC_MEM_ERR;
// }
//#else
// databuf=(u16*)bmpreadbuf;
// f_bmp=&f_bfile;
//#endif
// bmpheadsize=sizeof(hbmp);//得到bmp文件头的大小
// mymemset((u8*)&hbmp,0,sizeof(hbmp));//置零空申请到的内存.
// hbmp.bmiHeader.biSize=sizeof(BITMAPINFOHEADER);//信息头大小
// hbmp.bmiHeader.biWidth=width; //bmp的宽度
// hbmp.bmiHeader.biHeight=height; //bmp的高度
// hbmp.bmiHeader.biPlanes=1; //恒为1
// hbmp.bmiHeader.biBitCount=16; //bmp为16位色bmp
// hbmp.bmiHeader.biCompression=BI_BITFIELDS;//每个象素的比特由指定的掩码决定。
// hbmp.bmiHeader.biSizeImage=hbmp.bmiHeader.biHeight*hbmp.bmiHeader.biWidth*hbmp.bmiHeader.biBitCount/8;//bmp数据区大小
//
// hbmp.bmfHeader.bfType=((u16)'M'<<8)+'B';//BM格式标志
// hbmp.bmfHeader.bfSize=bmpheadsize+hbmp.bmiHeader.biSizeImage;//整个bmp的大小
// hbmp.bmfHeader.bfOffBits=bmpheadsize;//到数据区的偏移
// hbmp.RGB_MASK[0]=0X00F800; //红色掩码
// hbmp.RGB_MASK[1]=0X0007E0; //绿色掩码
// hbmp.RGB_MASK[2]=0X00001F; //蓝色掩码
// if(mode==1)res=f_open(f_bmp,(const TCHAR*)filename,FA_READ|FA_WRITE);//尝试打开之前的文件
// if(mode==0||res==0x04)res=f_open(f_bmp,(const TCHAR*)filename,FA_WRITE|FA_CREATE_NEW);//模式0,或者尝试打开失败,则创建新文件
// if((hbmp.bmiHeader.biWidth*2)%4)//水平像素(字节)不为4的倍数
// {
// bi4width=((hbmp.bmiHeader.biWidth*2)/4+1)*4;//实际要写入的宽度像素,必须为4的倍数.
// }else bi4width=hbmp.bmiHeader.biWidth*2; //刚好为4的倍数
// if(res==FR_OK)//创建成功
// {
// res=f_write(f_bmp,(u8*)&hbmp,bmpheadsize,&bw);//写入BMP首部
// for(ty=y+height-1;hbmp.bmiHeader.biHeight;ty--)
// {
// pixcnt=0;
// for(tx=x;pixcnt!=(bi4width/2);)
// {
// if(pixcnt<hbmp.bmiHeader.biWidth)databuf[pixcnt]=LCD_ReadPoint(tx,ty);//读取坐标点的值
// else databuf[pixcnt]=0Xffff;//补充白色的像素.
// pixcnt++;
// tx++;
// }
// hbmp.bmiHeader.biHeight--;
// res=f_write(f_bmp,(u8*)databuf,bi4width,&bw);//写入数据
// }
// f_close(f_bmp);
// }
//#if BMP_USE_MALLOC == 1 //使用malloc
// pic_memfree(databuf);
// pic_memfree(f_bmp);
//#endif
// return res;
return 0;
}