?? jpegdecode.c
字號:
#include "jpegbmp.h"
#include "lcd.h"
//////////////////////////////////////////////////////////////////////////////////
//Mini STM32開發板
//JPG/JPEG/BMP圖片顯示 解碼代碼
//正點原子@ALIENTEK
//技術論壇:www.openedv.com
//修改日期:2010/6/18
//版本:V1.4
//版權所有,盜版必究。
//Copyright(C) 正點原子 2009-2019
//All rights reserved
//********************************************************************************
//SRAM縮減優化后,消耗SRAM:12288BYTE
//移植到了SD卡上,可以解碼JPEG/JPG和BMP
//同時支持兩種格式的圖片顯示了
//對代碼進行了初步優化
//對JPEG格式,把不應該顯示的列和行的顏色計算省去了.
//在顯示大圖片的時候,速度明顯提高
//對BMP,優化了顯示支持,對大圖片的行和列也進行了取舍
//把不應該顯示的行和列去掉.同時把LCD顯示模式設置為自動
//添加,提高了速度.
//對BMP,支持16bit 24bit 和32bit圖片顯示
//對JPG,基本只支持JFIF格式
//如果不是JFIF格式,用windows的畫圖打開然后保存一下,就是這個格式了!
//////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////
//全局變量聲明,BMP和JPEG共用
FileInfoStruct *CurFile;//當前解碼/操作的文件
//圖像信息
typedef struct
{
u32 ImgWidth; //圖像的實際寬度和高度
u32 ImgHeight;
u32 Div_Fac; //縮放系數 (擴大了10000倍的)
u32 S_Height; //設定的高度和寬度
u32 S_Width;
u32 S_XOFF; //x軸和y軸的偏移量
u32 S_YOFF;
u32 staticx; //當前顯示到的xy坐標
u32 staticy;
}PIC_POS;
PIC_POS PICINFO;//圖像位置信息
////////////////////////////////////////////////////
void AI_Drow_Init(void); //智能畫圖,初始化.得到比例因子PICINFO.Div_Fac
/////////////////////////////////////////////////////
//在JPEG函數里面用到的變量
short SampRate_Y_H,SampRate_Y_V;
short SampRate_U_H,SampRate_U_V;
short SampRate_V_H,SampRate_V_V;
short H_YtoU,V_YtoU,H_YtoV,V_YtoV;
short Y_in_MCU,U_in_MCU,V_in_MCU;
unsigned char *lp;//取代lpJpegBuf
short qt_table[3][64];
short comp_num;
u8 comp_index[3];
u8 YDcIndex,YAcIndex,UVDcIndex,UVAcIndex;
u8 HufTabIndex;
short *YQtTable,*UQtTable,*VQtTable;
short code_pos_table[4][16],code_len_table[4][16];
unsigned short code_value_table[4][256];
unsigned short huf_max_value[4][16],huf_min_value[4][16];
short BitPos,CurByte;//byte的第幾位,當前byte
short rrun,vvalue;
short MCUBuffer[10*64];
short QtZzMCUBuffer[10*64];
short BlockBuffer[64];
short ycoef,ucoef,vcoef;
BOOL IntervalFlag;
short interval=0;
short Y[4*64],U[4*64],V[4*64];//
DWORD sizei,sizej;
short restart;
long iclip[1024];//4k BYTES
long *iclp;
//反Z字形編碼表
const int Zig_Zag[8][8]={{0,1,5,6,14,15,27,28},
{2,4,7,13,16,26,29,42},
{3,8,12,17,25,30,41,43},
{9,11,18,24,31,40,44,53},
{10,19,23,32,39,45,52,54},
{20,22,33,38,46,51,55,60},
{21,34,37,47,50,56,59,61},
{35,36,48,49,57,58,62,63}
};
const BYTE And[9]={0,1,3,7,0xf,0x1f,0x3f,0x7f,0xff};
//數據緩沖區
u8 jpg_buffer[1024];//數據緩存區
/////////////////////////////////////////////////////
//初始化智能畫點
void AI_Drow_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*=10000;//擴大10000倍
PICINFO.Div_Fac=temp1;
PICINFO.staticx=500;
PICINFO.staticy=500;//放到一個不可能的值上面
}
//判斷這個像素是否可以顯示
//(x,y) :像素原始坐標
//chg :功能變量.
//返回值:0,不需要顯示.1,需要顯示
__inline u8 IsElementOk(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;
}
//智能畫圖
//FileName:要顯示的圖片文件 BMP/JPG/JPEG
//(sx,sy) :開始顯示的坐標點
//(ex,ey) :結束顯示的坐標點
//圖片在開始和結束的坐標點范圍內顯示
BOOL AI_LoadPicFile(FileInfoStruct *FileName,u16 sx,u16 sy,u16 ex,u16 ey)
{
int funcret;//返回值
//得到顯示方框大小
if(ey>sy)PICINFO.S_Height=ey-sy;
else PICINFO.S_Height=sy-ey;
if(ex>sx)PICINFO.S_Width=ex-sx;
else PICINFO.S_Width=sx-ex;
//顯示區域無效
if(PICINFO.S_Height==0||PICINFO.S_Width==0)
{
PICINFO.S_Height=LCD_H;
PICINFO.S_Width=LCD_W;
return FALSE;
}
//影響速度
//SD_Init();//初始化SD卡,在意外拔出之后可以正常使用
//顯示的開始坐標點
PICINFO.S_YOFF=sy;
PICINFO.S_XOFF=sx;
//文件名傳遞
CurFile=FileName;
if(CurFile->F_Type==T_BMP)//得到一個BMP圖像
{
funcret=BmpDecode(CurFile); //得到一個BMP圖像
return funcret;
}
else if(CurFile->F_Type==T_JPG||CurFile->F_Type==T_JPEG)//得到JPG/JPEG圖片
{
//得到JPEG/JPG圖片的開始信息
F_Open(CurFile);
//開始時讀入1024個字節到緩存里面.方便后面提取JPEG解碼的信息
F_Read(CurFile,jpg_buffer); //讀第一次
F_Read(CurFile,jpg_buffer+512);//讀第二次
InitTable(); //初始化各個數據表
if((funcret=InitTag())!=FUNC_OK)return FALSE;//初始化表頭不成功
if((SampRate_Y_H==0)||(SampRate_Y_V==0))return FALSE ;//采樣率錯誤
AI_Drow_Init(); //初始化PICINFO.Div_Fac,啟動智能畫圖
funcret=Decode();//解碼JPEG開始
}else return FALSE; //非圖片格式!!!
if(funcret==FUNC_OK)return TRUE;//解碼成功
else return FALSE; //解碼失敗
}
//解碼這個BMP文件
BOOL BmpDecode(FileInfoStruct *BmpFileName)
{
u16 count;
u8 rgb ,color_byte;
u16 x ,y,color,tmp_color ;
u16 uiTemp; //x軸方向像素計數器
u16 countpix=0;//記錄像素
//x,y的實際坐標
u8 realx=0;
u16 realy=0;
u8 yok=1;
BITMAPINFO *pbmp;//臨時指針
CurFile=BmpFileName;
F_Open(CurFile);//打開文件
F_Read(CurFile,jpg_buffer);//讀出512個字節
pbmp=(BITMAPINFO*)jpg_buffer;//得到BMP的頭部信息
count=pbmp->bmfHeader.bfOffBits; //數據偏移,得到數據段的開始地址
color_byte=pbmp->bmiHeader.biBitCount/8;//彩色位 16/24/32
PICINFO.ImgHeight=pbmp->bmiHeader.biHeight;//得到圖片高度
PICINFO.ImgWidth=pbmp->bmiHeader.biWidth; //得到圖片寬度
//水平像素必須是4的倍數!!
if((PICINFO.ImgWidth*color_byte)%4)
uiTemp=((PICINFO.ImgWidth*color_byte)/4+1)*4;
else
uiTemp=PICINFO.ImgWidth*color_byte;
AI_Drow_Init();//初始化智能畫圖
//開始解碼BMP
x =0 ;
y=PICINFO.ImgHeight;
rgb=0;
realy=y*PICINFO.Div_Fac/10000;
while(1)
{
while(count<512) //讀取一簇512扇區 (SectorsPerClust 每簇扇區數)
{
if(color_byte==3) //24位顏色圖
{
switch (rgb)
{
case 0:
tmp_color = jpg_buffer[count]>>3 ;
color |= tmp_color;
break ;
case 1:
tmp_color = jpg_buffer[count]>>2 ;
tmp_color <<= 5 ;
color |= tmp_color ;
break;
case 2 :
tmp_color = jpg_buffer[count]>>3 ;
tmp_color <<= 11 ;
color |= tmp_color ;
break ;
}
}
else
{
if(color_byte==2) //16位顏色圖
{
switch(rgb)
{
case 0 :
color=jpg_buffer[count]&0x1f;
tmp_color=jpg_buffer[count]>>5;
tmp_color<<=6;
color|=tmp_color;
break ;
case 1 :
tmp_color=jpg_buffer[count];
tmp_color<<=9 ;
color |= tmp_color ;
break ;
}
}
else
{
if(color_byte==4)//32位顏色圖
{
switch (rgb)
{
case 0 :
tmp_color=jpg_buffer[count];
color|=tmp_color>>3;
break ;
case 1 :
tmp_color=jpg_buffer[count];
tmp_color>>=2;
color|=tmp_color<<5;
break ;
case 2 :
tmp_color=jpg_buffer[count];
tmp_color>>=3;
color|=tmp_color<<11;
break ;
case 3 :break ;
}
}
}
}//位圖顏色得到
rgb++;
count++ ;
if(rgb==color_byte) //水平方向讀取到1像素數數據后顯示
{
if(x<PICINFO.ImgWidth)
{
realx=x*PICINFO.Div_Fac/10000;//x軸實際值
if(IsElementOk(realx,realy,1)&&yok)//符合條件
{
POINT_COLOR=color;
LCD_DrawPoint(realx+PICINFO.S_XOFF,realy+PICINFO.S_YOFF-1);
}
}
x++;//x軸增加一個像素
color=0x00;
rgb=0;
}
countpix++;//像素累加
if(countpix>=uiTemp)//水平方向像素值到了.換行
{
y--;
if(y<=0)return TRUE;
realy=y*PICINFO.Div_Fac/10000;//實際y值改變
if(IsElementOk(realx,realy,0))yok=1;//此處不改變PICINFO.staticx,y的值
else yok=0;
x=0;
countpix=0;
color=0x00;
rgb=0;
}
}
if(!F_Read(CurFile,jpg_buffer))break;//讀出512個字節,讀數失敗時自動退出
count=0 ;
}
return TRUE;//BMP顯示結束.
}
//對指針地址進行改變!
//pc :當前指針
//返回值:當前指針的減少量.在d_buffer里面自動進行了偏移
unsigned int P_Cal(unsigned char*pc)
{
unsigned short cont=0;//計數器
unsigned long buffer_val=0; //寄存區首地址
unsigned long point_val=0; //指針所指的當前地址
unsigned char secoff;
unsigned short t;
unsigned char *p;
p=jpg_buffer+512;//偏移到中間
point_val=(unsigned long)pc;//得到當前指針所指地址
buffer_val=(unsigned long)&jpg_buffer;//得到緩存區首地址
cont=point_val-buffer_val;//得到兩者之差
if(cont>=512)//數據超過了中間
{
secoff=cont/512;//超出了多少secoff個512字節
while(secoff) //讀取secoff次512個字節
{
for(t=0;t<512;t++)jpg_buffer[t]=p[t];//復制后512個字節 給前512個字節
if(!F_Read(CurFile,p))//讀取512個字節到d_buffer的后半部分
{//讀取結束了
//printf("read Fail!\n");
break;//讀數失敗!break;
}
secoff--;
}
}
return cont-cont%512;//指針地址縮減
}
//初始化d_buffer的數據
int InitTag(void)
{
BOOL finish=FALSE;
u8 id;
short llength;
short i,j,k;
short huftab1,huftab2;
short huftabindex;
u8 hf_table_index;
u8 qt_table_index;
u8 comnum;//最長為256個字節
unsigned char *lptemp;
short colorount;
lp=jpg_buffer+2;//跳過兩個字節SOI(0xFF,0xD8 Start of Image)
lp-=P_Cal(lp);
while (!finish)
{
id=*(lp+1);//取出低位字節(高位在前,低位在后)
lp+=2; //跳過取出的字節
lp-=P_Cal(lp);
switch (id)
{
case M_APP0: //JFIF APP0 segment marker (0xE0)
//標志應用數據段的開始
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -