Files
player/Project/Src/JPEG/bmp.c
2025-07-05 19:47:28 +08:00

532 lines
15 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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