图片播放器小项目(详解)
以下內容源于朱有鵬《物聯網大講堂》課程的學習整理,如有侵權,請告知刪除。
一、開始動手寫代碼
1、Makefile介紹
(1)這是一個通用的項目管理的Makefile體系,自己寫的(有子文件夾組織的)項目可以直接套用這套Makefile體系。
(2)包含三類:頂層Makefile、Makefile.build(完全不需要改動)、子文件夾下面的Makefile。
- 子文件夾下的Makefile一般是類似下面的內容。
(3)可以參考:http://www.cnblogs.com/lidabo/p/4521123.html。
2、使用SI建立工程
二、framebuffer基本操作代碼
顯示圖片,需要framebuffer。
三、圖片顯示原理和實踐
1、圖片顯示原理
(1)概念1:像素
(2)概念2:點陣(像素點構成的陣列)
(3)分辨率
- 物理分辨率:物理屏幕像素點的真實個數;
- 顯示分辨率:可以小于等于物理分辨率;(通過抽樣)
(4)清晰度(與分辨率和像素間距有關,主觀概念)
- 像素間距相同時(物理尺寸固定了,則像素間距就固定了),分辨率越大越清晰;分辨率相同時,像素間距越小越清晰。
(5)bpp(RGB565、RGB888)
- 像素深度,每個像素用多少bit數據表示。
- 一般每個像素點使用32bit(4個字節),但一般是24位色(高八位一般沒用或者用著其他標識,用00或者ff填充以達到內存對齊);
- RGB888表示紅用8位,綠8位,藍8位。
(6)顏色序(RGB、BGR)
2、圖片點陣數據獲取
(1)Image2LCD軟件提取
(2)通過圖片/視頻文件直接代碼方式提取
四、圖片數據提取和顯示
1、Image2LCD提取圖片數據
(1)軟件下載:http://www.cr173.com/soft/43222.html
(2)軟件使用
- 一般不要圖像頭數據,只需要純數據;
- 一般水平掃描;
- 一般選24位真彩色(即RGB888);
- 選1024*600后,點右邊按鈕更新;
- 輸出圖像調整中,可以調整RGB。
- 最后點擊保存。
2、圖片顯示編碼與實踐
void fb_draw_picture(void) {unsigned char* pdata=gImage_1024600;unsigned int i,j,cnt;unsigned int*p=pfb;for(i=0;i<HEIGHT;i++){for(j=0;j<WIDTH;j++){cnt=i*WIDTH+j;cnt*=3;*p=((pdata[cnt+0]<<0)|(pdata[cnt+1]<<8)|(pdata[cnt+2]<<16));//可以在這里修改,達到正確的顯示(當RB相反時)p++;}} }五、圖片顯示的高級話題
1、RGB順序調整
(1)RGB順序有三個地方都有影響
- 第一個是fb驅動中的排布;
- 第二個是應用程序中的排布;
- 第三個是圖像數據本身排布(Image2LCD中調整RGB順序);
(2)如果三個點中RGB順序是一致的就會顯示正常。如果三個設置不一致就可能會導致顯示結果中R和B相反了;
(3)實際寫程序時,一般不去分析這東西,而是根據實際顯示效果去調。如果反了就去調正應用程序中的RGB順序就行了,這樣最簡單。
2、顯示函數的其他寫法
void fb_draw_picture2(void) {unsigned char* pdata=gImage_1024600;unsigned int x,y,cnt;for(y=0;y<HEIGHT;y++){for(x=0;x<WIDTH;x++){cnt=y*WIDTH+x;*(pfb+cnt)=((pdata[cnt*3+0]<<16)|(pdata[cnt*3+1]<<8)|(pdata[cnt*3+2]<<0));//這里的像素矩陣和cnt有線性關系,所以可以這樣寫}} }六、任意分辨率大小圖片顯示
(1)圖片比屏幕分辨率大
- 這種情況下多出來的部分肯定是沒法顯示的。處理方法是直接在顯示函數中把多余不能被顯示的部分給丟掉。
(2)圖片大小比屏幕大小要小
- 這種情況下圖片只是填充屏幕中一部分,剩余部分仍然保持原來的底色。
- 在獲取圖片數據時,大小和圖片實際大小在這里是一致的。假如不一致呢?
七、任意起點位置圖片顯示
1、小圖片任意起點(但整個圖片顯示沒有超出屏幕范圍內)
算法1:
void fb_draw_picture4(unsigned int x0,unsigned int y0) {unsigned char* pdata=gImage_500281;unsigned int x,y,cnt;unsigned int cnt1,cnt2;for(y=y0;y<281+y0;y++){for(x=x0;x<500+x0;x++){cnt1=y*WIDTH+x;/*cnt始終都是fb 緩沖區整體中的像素點的編號*/cnt2=500*(y-y0)+(x-x0);*(pfb+cnt1)=((pdata[cnt2*3+0]<<16)|(pdata[cnt2*3+1]<<8)|(pdata[cnt2*3+2]<<0));/*左值考慮當前像素點在fb中的偏移量*//*右值考慮當前像素點在圖像數據數組的下標(這里是三個為一個元素的數組的下標,因此上面會*3)*/}} }
算法2:(因為每循環一次,+3)
2、起點導致圖片超出屏幕外
(1)現象
- 左右超出去,會在相反方向補全:這是內部for循環可能超過1024的界定(但沒有超出fb的大小)造成的。
- 上下超出去,則會消失:因為雙緩沖進到了另一幀。如果沒有雙緩沖,則會內存溢出。
(2)修改代碼,使得超出部分不再顯示。
法一:
void fb_draw_picture5(unsigned int x0,unsigned int y0) {unsigned char* pdata=gImage_500281;unsigned int x,y,cnt;unsigned int a=0;for(y=y0;y<281+y0;y++){if(y>=HEIGHT)//y方向超出{a+=3;break;//最后一行已經顯示了,剩下的不用考慮了}for(x=x0;x<500+x0;x++){if(x>=WIDTH)//x方向超出了{a+=3;continue;}cnt=y*WIDTH+x;/*cnt始終都是像素點的編號*/*(pfb+cnt)=((pdata[a+0]<<16)|(pdata[a+1]<<8)|(pdata[a+2]<<0));a+=3;/*因此,像素點矩陣也應該三個三個地傳送進來*/}} } 法二:
void fb_draw_picture4(unsigned int x0,unsigned int y0) {unsigned char* pdata=gImage_500281;unsigned int x,y,cnt;unsigned int cnt1,cnt2;for(y=y0;y<281+y0;y++){for(x=x0;x<500+x0;x++){if(x>=WIDTH)//x方向超出了{///a+=3;注意這里被詮釋了continue;}cnt1=y*WIDTH+x;/*cnt始終都是fb 緩沖區整體中的像素點的編號*/cnt2=500*(y-y0)+(x-x0);*(pfb+cnt1)=((pdata[cnt2*3+0]<<16)|(pdata[cnt2*3+1]<<8)|(pdata[cnt2*3+2]<<0));/*左值考慮當前像素點在fb中的偏移量*//*右值考慮當前像素點在圖像數據數組的下標*/}} }
八、BMP圖片的顯示
1、圖片文件的本質
(1)圖片文件是二進制文件。
- 文件分兩種,即二進制文件、文本文件;
(2)不同格式的圖片文件的差別。
- 圖片被壓縮與否的區別。
(3)BMP圖片的基本特征
- 未被壓縮的元素位圖格式(bit map)。
2、BMP圖片詳解
(1)如何識別BMP文件?
- 每種圖片格式都有定義好的一種識別方法,BMP圖片特征是以0x424D開頭;
(2)BMP文件組成
- 頭信息+有效信息
3、BMP文件頭信息、圖片有效數據區
見博客:https://www.2cto.com/kf/201310/252434.html
(1)文件大小
(2)有效數據開始的位置
(3)信息頭的大小:40個字節
(4)分辨率
(5)24位真彩色
(6)……
4、寫代碼解析BMP圖片
第一步:打開BMP圖片
第二步:判斷圖片格式是否真是BMP
第三步:解析頭信息,得到該BMP圖片的詳細信息
第四步:根據第三步得到的信息,去合適位置提取真正的有效圖片信息
第五步:將得到的有效數據丟到fb中去顯示
這樣實際比較繁瑣!使用結構體比較好!
//path是bmp圖片的pathname //該函數解析path圖片,并將圖片數據丟到bmp_buf中 //返回值錯誤時返回-1,正確返回0 int bmp_analyze(unsigned char *path) {int fd=-1;unsigned char buf[54]={0};ssize_t ret=0;//打開bmp文件fd=open(path,O_RDONLY);if(fd<0){fprintf(stderr,"open %s error.\n",path);return -1;}//讀取文件頭信息ret=read(fd,buf,54);if(ret!=54){fprintf(stderr,"read file header error.\n");return -1;}//解析頭//判斷是否BMP圖片if(buf[0]!='B'||buf[1]!='M'){fprintf(stderr,"file %s is not a bmp picture.\n",path);return -1;}printf("file %s is a bmp picture.\n",path);printf("width is %d\n",*((unsigned int*)(buf+0x12)));printf("hith is %d\n",*((unsigned int*)(buf+0x16)));//成功則說明小端模式close(fd);return 0;}
5、用結構體方式解析BMP文件頭
6、用結構體方式解析BMP信息頭
#ifndef __BMP_H__ #define __BMP_H__typedef struct {unsigned char bfType[2];//文件類?unsigned long bfSize; //位圖大小 unsigned short bfReserved1; //位0 unsigned short bfReserved2; //位0 unsigned long bfOffBits;//到數據偏移量 }__attribute__((packed))BitMapFileHeader;//使編譯器不優化,其大小為14字節 //信息頭結構體 typedef struct { unsigned long biSize;// BitMapFileHeader 字節數long biWidth;//位圖寬度 long biHeight;//位圖高度,正位正向,反之為倒圖 unsigned short biPlanes;//為目標設備說明位面數,其值將總是被設為1unsigned short biBitCount;//說明比特數/象素,為1、4、8、16、24、或32。 unsigned long biCompression;//圖象數據壓縮的類型沒有壓縮的類型:BI_RGB unsigned long biSizeImage;//說明圖象的大小,以字節為單位 long biXPelsPerMeter;//說明水平分辨率 long biYPelsPerMeter;//說明垂直分辨率 unsigned long biClrUsed;//說明位圖實際使用的彩色表中的顏色索引數unsigned long biClrImportant;//對圖象顯示有重要影響的索引數,0都重要。 } __attribute__((packed)) BitMapInfoHeader; #endif void fb_draw(const unsigned char* ppic) {unsigned char* pdata=ppic;unsigned int x,y;unsigned int a=0;unsigned int cnt=0;a=281*500*3-3;for(y=0;y<281;y++){for(x=0;x<500;x++){cnt=y*WIDTH+x;*(pfb+cnt)=((pdata[a+0]<<16)|(pdata[a+1]<<8)|(pdata[a+2]<<0));a-=3;}//a表示每個像素的三個字節數據的首字節編號} } //path是bmp圖片的pathname //該函數解析path圖片,并將圖片數據丟到bmp_buf中 //返回值錯誤時返回-1,正確返回0 int bmp_analyze(unsigned char *path) {int fd=-1;BitMapFileHeader fheader ;BitMapInfoHeader info;ssize_t ret=0;int i;unsigned long len;//打開bmp文件fd=open(path,O_RDONLY);if(fd<0){fprintf(stderr,"open %s error.\n",path);return -1;}ret= read(fd,&fheader,sizeof(fheader));printf("bfsize =%d.\n",fheader.bfSize);//位圖大小printf("boffsize =%d.\n",fheader.bfOffBits);//有效信息偏移量#if 0for( i=0;i<sizeof(fheader);i++){printf("%x ",*((unsigned char *)&fheader+i));}printf("\n"); #endifret= read(fd,&info,sizeof(info));/*會沿著繼續讀下去*/printf("picture resolution : %d * %d.\n",info.biHeight,info.biWidth);printf("picture bpp : %d \n",info.biBitCount);//讀取圖片有效信息//先把文件指針移動到有效信息的偏移量處(lseek函數)//然后讀出info.biHeight*info.biWidth*info.biBitCount/8個字節lseek(fd,fheader.bfOffBits,SEEK_SET);//從文件開始的位置開始len=info.biHeight*info.biWidth*info.biBitCount /8;printf("len put to buff :%ld.\n",len);read(fd,bmp_buf,len);//把內容丟到fb中去顯示fb_draw(bmp_buf);/*數據和使用image2lcd獲取的不一樣*這里會旋轉180度顛倒*即第一個像素放到最后一個像素點像素,依次類推了*/close(fd);return 0; }
注意:
- 使用結構體來解析圖片數據時,和使用image2lcd不一樣。
- 這里會旋轉180度顛倒,即第一個像素放到最后一個像素點像素,依次類推了。
- 因此在fb_draw()函數中,需要將最后一個像素點數據放在第一個像素處顯示,以此類推。
---------------以上代碼寫得不規整,要進行規整--------------------
九、及時規整
1、再次強調規范問題
(1)函數、變量起名字要合法合理;
- 小寫函數;
(2)要寫注釋;
- 第一步,第二步……可以先寫注釋再寫代碼;
(3)函數長短要合適;
(4)多文件組織,每個東西丟到合理的位置
2、為什么要規整項目?
(1)完全自由寫項目時不可能一步到位,只能先重內容和功能,后補條理和規范;
(2)規整的過程也是一個梳理邏輯和分析架構的過程。
3、對本項目進行規整
(1)去掉測試顯示時頭文件形式提供的圖片顯示相關的東西
- 即去除fb_draw_picture_n()測試函數,因為我們的最終目的是解析bmp格式圖片(已經實現了,因此要刪除之前的測試函數)
4、一些重構代碼的技巧
(1)用#if 0 ? #endif來屏幕不需要的代碼,不要用/* ?*/;
(2)暫時不要的代碼先不要刪除,而是屏幕掉
5、添加DEBUG宏以控制調試信息輸出
debug宏添加好后,要使能輸出可以有2種方式:
- 第一種:在debug宏定義之前定義DEBUG宏。見http://blog.csdn.net/oqqhutu12345678/article/details/78873195
- 第二種:在編譯參數中添加-DDEBUG編譯選項。(在makefile中添加)
6、圖片信息用結構體來封裝傳遞
//封裝圖片各種信息 typedef struct pic_info {char* pathname;//路徑和文件名字unsigned int width;unsigned int height;unsigned int bpp;unsigned char *pData;//指向圖片有效數據存儲的buff}pic_info;從而需要修改相關代碼。
十、jpg圖片的顯示原理分析
1、認識jpg圖片
(1)屬于二進制文件。
(2)有其固定的識別特征:http://www.cnblogs.com/Wendy_Yu/archive/2011/12/27/2303118.html
(3)是經過壓縮的圖片格式。
2、jpg圖片如何顯示
(1)jpg圖片中的二進制數并不對應像素數據。
(2)LCD顯示器的接口仍然是framebuffer。
(3)要顯示jpg圖片必須先解碼jpg得到相應的位圖數據。
3、如何解碼jpg圖片
(1)圖片編碼和解碼對應著壓縮和解壓縮過程
(2)編碼和解碼其實就是一些數學運算(壓縮度、算法復雜度、時間、清晰度)
(3)軟件編解碼和硬件編解碼
- 需要頻繁進行編解碼的話,一般使用硬件編解碼。
(4)不同的圖片格式其實就是編解碼的算法不同,結果是圖片特征不同
(5)編程實戰:使用開源編解碼庫
十一、libjpeg介紹及開源庫的使用方法
1、libjpeg介紹
(1)基于linux的開源軟件;
(2)C語言編寫(gcc、使用Makefile管理);
(3)提供JPEG圖片的編解碼算法實現;
2、libjpeg版本及下載資源
(1)經典版本v6b:https://sourceforge.net/projects/libjpeg/files/libjpeg/6b/
(2)最新版本v9b:http://www.ijg.org/
3、開源庫的使用方法
(1)移植(源碼下載、解壓、配置、修改Makefile、編譯或交叉編譯)
- 移植的目的是由源碼得到三個東西:動態庫.so,靜態庫.a,頭文件.h。
(2)部署(部署動態庫so、部署靜態庫.a和頭文件.h)
- 動態庫是程序在運行時才需要的,編譯程序時不需要。
- 靜態庫是靜態連接時才需要,動態鏈接時不需要。
- 頭文件.h是在編譯程序時使用的,運行時不需要的。
- 靜態庫和頭文件,是在編譯鏈接過程中需要的,因此要把靜態庫.a文件和頭文件.h文件放到ubuntu的文件系統中。
- 動態庫是在運行時需要的,所以動態庫so文件要放到開發板的文件系統中(放的過程就叫部署),。
(3)注意三個編譯鏈接選項:-I ?-l(小L) ?-L
舉例如-I:
- -I是編譯選項(準確的是說是makefile預處理選項CFLAGS或者CPPFLAGS中指定),用來指定預處理時查找頭文件的范圍的。
- -l(小寫L)是鏈接選項(LDFLAGS中指定),用來指定鏈接額外的庫的名字(譬如我們用到了數學函數,就用-lm,鏈接器就會去鏈接libm.so;那么我們使用了libjpeg,對應的庫名字就叫libjpeg.so,就需要用-ljpeg選項去鏈接)。
- -L是鏈接選項(LDFLAGS中指定),用來指定鏈接器到哪個路徑下面去找動態鏈接庫。
- 總結:-l(小寫L)是告訴鏈接器要鏈接的動態庫的名字,而-L是告訴鏈接器要鏈接的動態庫的路徑。
十二、libjpeg的移植實戰
1、移植
(1)源碼下載、解壓至/tmp/decodeporting目錄下。
(2)編譯前的配置(分析configure文件的usage、借鑒前輩的設置,得到應該執行下面命令)
- ./configure --prefix=/opt/libcode --exec-prefix=/opt/libcode --enable-shared --enable-static-build=i386 -host=arm
- ? ? ? ? ? ? ? ? ? 指定編譯結果放置的目錄(如果沒有則需要先建立相應的目錄)
- 配置生成了makefile文件,通過查看知道需要在/opt/libcode中創建include、bin、lib目錄。
(3)Makefile檢查,主要查看交叉編譯設置是否正確
- CC=gcc 改為 CC=arm-linux-gcc //編譯器,如果不改,只能在ubuntu使用,不能在開發板使用。
- AR=ar rc 改為 AR=arm-linux-ar rc
- AR2=ranlib 改為 AR2=arm-linux-ranlib
(4)編譯:執行make命令。
(5)安裝
- 執行make install-lib;
- 安裝就是將編譯生成的庫文件、頭文件、可執行文件分別裝載到--prefix ?--exec-prefix所指定的那些目錄中去。
2、部署
前面只是完成移植,相關文件都在ubuntu上。我們要把動態鏈接庫(.so文件)放到開發板的根文件系統中(/root/rootfs/),可以考慮三者之一:
- 第一個:/lib
- 第二個:/usr/lib(前兩個在程序運行時,可以自動找到。后面任意目錄時,需要在程序中指定)
- 第三個:任意指定目錄
十三、使用libjpeg解碼顯示jpg圖片
1、如何使用一個新的庫
(1)思路一:網絡上找別人使用過后寫的文檔、博客等作為參考。(2)思路二:看庫源碼自帶的文檔(說明文檔和示例代碼)。我們從思路二出發。
2、libjpeg說明文檔和示例代碼
(1)說明文檔:README和libjpeg.doc(2)示例代碼:example.c
3、結合說明文檔來實踐
4、解讀example.c和移植
5、代碼問題
(1)測試代碼,先試圖讀取jpg圖片頭信息。
(2)問題排除- 編譯時問題:主要就是頭文件包含,除了在代碼中包含頭文件外,還要注意指明頭文件的路徑。
- 因為盡管包含了頭文件,但只會在當前的目錄和path指定的路徑中尋找。我們應該在總Makefile中指定。
- 注意-I、-l、-L三個編譯鏈接選項的使用
6、部署動態庫以使程序運行起來
(1)一般放到開發板根文件系統/lib或者/usr/lib下
- 這樣不需要給系統指定庫路徑,就能自動找到。
- 強調一下是開發板根文件系統下的路徑,千萬不要弄成了ubuntu的根文件系統下的目錄。
(2)放到自定義的第三方的目錄
- 將該自定義第三方目錄導出到環境變量LD_LIBRARY_PATH下即可。
- 可以使用echo $LD_LIBRARY_PATH查看當前的路徑環境變量包含哪些路徑。
- 可以把上述操作寫進到run.sh文件中,省得每次都要這樣操作。
7、測試讀取頭信息
十四、解決解碼顯示中的問題
1、問題分析及解決記錄
(1)根據LCD錯誤的顯示狀態,分析有可能是顯示函數fb_draw中的圖片寬高數據有誤,于是在fb_draw函數中添加debug打印出寬和高來。結果發現是對的。
(2)顯示函數中的圖片寬高和fb寬高都是對的,結果顯示時還是只有一溜(其余位置黑屏),可能的一個原因就是:顯示數據本身不對,很多都是0。
- 如何驗證?只要把顯示數據打印出來看一看就知道了。
- 結果發現打印出的待顯示數據果然是很多0,說明給顯示函數的待顯示數據就是錯的。
(3)這些待顯示數據為什么會錯?
- 第一種可能性就是libjpeg解碼出來的數據就是錯的;
- 第二種可能性是解碼一行出來暫存到buffer的時候,或者memcpy從暫存的buffer拿出來給pData指向的空間的時候給搞錯了。
- 相對來說第二種很好驗證而第一種不好驗證。只需要在jpeg_read_scanlines函數后面直接打印顯示解碼出來的一行數據,就可以知道是不是第二種情況。
- 結果打印出來好多0,說明是第一種情況。
(4)截至目前已經鎖定問題,就是jpeg_read_scanlines解碼出來的數據本身就不對。
(5)可能的問題
- 有可能是libjpeg本身就有問題;
- 有可能我們對libjpeg的部署不對導致他工作不對;
- 有可能我們寫的代碼不對,也就是說我們沒用正確的方法來使用libjpeg。
(6)沒有思路怎么辦
- 去網上找一些別人寫的libjpeg解碼顯示圖片的示例代碼,多看幾個,對著和我們的關鍵部位對比,尋找思路。
- 如果在網上找不到相關資料,這時候就只有硬著頭皮去看源碼了。譬如去libjpeg的源碼中查看:jpeg_read_scanlines、cinfo.mem->alloc_sarray等。
(7)解決了buffer申請導致的問題之后,我們再來解決2個遺留的問題
- 一個就是RGB順序問題,另一個是圖像轉了180度的問題。
(8)添加了fb_draw2函數并且調用后,2個遺留問題徹底解決。至此,jpg圖片顯示完美實現。
2、結束jpg圖片部分
(1)加上jpg圖片格式識別:判斷開頭和結尾的特征字節。
(2)對外封裝好用的jpg圖片顯示函數
(3)對外封裝好用的bmp圖片顯示函數
十五、解碼顯示png圖片
1、思路分析
(1)png更像是jpg而不像是bmp;
(2)png和jpg都是壓縮格式的圖片,都是二進制文件,不同之處是壓縮和解壓縮的算法不同。
(3)通過libjpeg來編解碼jpg圖片,那么同樣有一個libpng用來編解碼png圖片。
(4)工作思路和順序
- 找到并移植并部署libpng,然后查readme和其他文檔示例代碼等來使用libpng提供的API來對png圖片進行解碼,并將解碼出來的數據丟到framebuffer中去顯示。
2、libpng移植
(1)下載源碼包:
(2)解壓、配置、修改Makefile、編譯、部署。注意實際路徑。
- ./configure --host=arm-linux --enable-shared --enable-static --prefix=/opt/libdecode
(3)配置出錯,報錯信息:configure: error: zlib not installed
- 分析問題是因為libpng依賴于zlib庫,所以要先移植zlib庫才可以。
(4)移植了zlib后再過來配置,還是報錯,原因是因為沒有導出相關環境變量,所以libpng在配置的時候找不到剛才移植的zlib庫的庫文件和頭文件。
(5)解決方案就是使用epport臨時性的導出,在scrt中輸入:
# export LDFLAGS="-L/opt/libdecode/lib" # export CFLAGS="-I/opt/libdecode/include" # export CPPFLAGS="-I/opt/libdecode/include"(6)導出后再次配置就過了,然后編譯和安裝
(7)make && make install
3、zlib移植
(1)下載:http://www.zlib.net/,并解壓
(2)配置:export CC=arm-linux-gcc ? ? ?
./configure -shared --prefix=/opt/libdecode
(3)make && make install
(4)make install后/opt/libdecode目錄下的lib和include目錄下就有了zlib的靜態庫動態庫和頭文件了,然后再回去繼續libpng的移植。
4、參考源碼包自帶的資料開始編程
(1)readme
(2)libpng-manual.txt
(3)example.c 和 pngtest.c
十六、圖片文件的管理和檢索
1、圖片文件的管理
(1)在物理磁盤存儲層次上,用一個文件夾來管理;(2)在程序中,用數據結構來管理。
- 用數組管理
- 用鏈表管理
(4)編程實戰(細節見代碼,下面是關鍵點)
a、新建一個文件夾,記得要在makefile中添加新建的文件夾路徑,以及在新建文件夾中新建makefile來管理文件。
b、文件夾的打開操作、讀取操作
2、圖片信息的自動檢索
(1)讀取文件類型
(2)普通文件和文件夾分類處理
(3)普通文件區分,將其中的圖片按格式存儲到圖片管理數組/鏈表中
typedef enum image_type {IMAGE_TYPE_BMP,IMAGE_TYPE_JPG,IMAGE_TYPE_UNKNOWN, }image_type_e;typedef struct image_info {char pathname[PATHNAME_LEN];image_type_e type; }image_info_t; int scan_image2(const char *path) {// 在本函數中遞歸檢索path文件夾,將其中所有圖片填充到iamges數組中去DIR *dir;struct dirent *ptr;char base[1000];struct stat sta;if ((dir = opendir(path)) == NULL){perror("Open dir error...");exit(1);}// readdir函數每調用一次就會返回opendir打開的basepath目錄下的一個文件,直到// basepath目錄下所有文件都被讀完之后,就會返回NULLwhile ((ptr = readdir(dir)) != NULL){if(strcmp(ptr->d_name, ".")==0 || strcmp(ptr->d_name, "..")==0) ///current dir OR parrent dircontinue;// 用lstat來讀取文件屬性并判斷文件類型memset(base,'\0',sizeof(base));//strcpy(base,path);strcat(base,"/");strcat(base,ptr->d_name);lstat(base, &sta);if (S_ISREG(sta.st_mode)){//printf("regular file.\n");//printf("d_name:%s/%s\n", path, ptr->d_name);// 如果是普通文件,就要在這里進行處理:// 處理思路就是 先判定是否屬于已知的某種圖片格式,如果是則放到images數組中// 如果都不屬于則不理他if (!is_bmp(base)){strcpy(images[image_index].pathname, base);images[image_index].type = IMAGE_TYPE_BMP;}if (!is_jpg(base)){strcpy(images[image_index].pathname, base);images[image_index].type = IMAGE_TYPE_JPG;}image_index++;}if (S_ISDIR(sta.st_mode)){//printf("directory.\n");//printf("d_name:%s/%s\n", path, ptr->d_name);scan_image2(base);}} } void show_images(void) {int i;for (i=0; i<image_index; i++){switch (images[i].type){case IMAGE_TYPE_BMP:display_bmp(images[i].pathname); break;case IMAGE_TYPE_JPG:display_jpg(images[i].pathname); break;default:break;}sleep(2);} }
十七、添加觸摸翻頁功能
1、讀取觸摸坐標數據
- 開發板上的觸摸屏是/dev/input/event2(我這里是event2)
2、使用觸摸坐標判斷并執行翻頁操作
(1)執行./run.sh后會阻塞,如果點擊觸摸屏,會在scrt中顯示測試的內容。
- code 0表示x坐標,value為x的值;code 1表示y坐標,value為y的值。
(2)在不同區域點一下,有不同的效果。
十八、總結與回顧
1、bug解決
2、項目總結
(1)項目描述:軟硬件平臺等
(2)重點和難點
3、項目展望與擴展功能
(1)劃屏翻頁
(2)圖片放大與縮小顯示
(3)動畫
(4)開機畫面
(5)背景音樂
總結
以上是生活随笔為你收集整理的图片播放器小项目(详解)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 最新最全 VSCODE 插件推荐(202
- 下一篇: 如何通过QQ机器人技术实现禅道bug的自