自制作業系統Antz——day13 顯示圖片
阿新 • • 發佈:2018-11-01
顯示圖片只是在多媒體課上看著bmp格式圖片的突發奇想,然後就實現在了我自己的作業系統
Antz系統更新地址
Linux核心原始碼分析地址
Github專案地址
效果圖:
顯示圖片的原理
在之前顯示卡操作時,螢幕上的畫素點我們是直接賦予一個顏色值的。
0xa0000是顯示屏左上角第一個畫素的地址,我們只需要根據地址賦予相應圖片的rgb值即可實現圖片的顯示。Antz使用的顯示卡模式只能支援255種顏色,也就是bmp中24色的圖片。
所以我們需要先將一個24色bmp格式的圖片進行rgb值讀取,然後再將rgb的值賦予到顯示卡的相應位置。
圖片rgb讀取
read.cpp
#include<cstdlib> #include<cstdio> #include<cmath> #include<iomanip> #include"read.h" using namespace std; unsigned int **out_r; unsigned int **out_g; unsigned int **out_b; void getRGB() { char readPath[] = "a.bmp"; readBmp(readPath); // 輸出整體影象資訊 cout << "\nwidth=" << bmpWidth << "\nheight=" << bmpHeight << "\nbiBitCount=" << biBitCount << endl; // 影象的位元組數 int linebyte1 = (bmpWidth*biBitCount / 8 + 3) / 4 * 4; int n = 0, m = 0, count_xiang_su = 0; int i ; out_r = new unsigned int *[bmpHeight]; for (i= 0; i<bmpHeight; i++) out_r[i] = new unsigned int[bmpWidth]; out_g = new unsigned int *[bmpHeight]; for (i = 0; i<bmpHeight; i++) out_g[i] = new unsigned int[bmpWidth]; out_b = new unsigned int *[bmpHeight]; for (i = 0; i<bmpHeight; i++) out_b[i] = new unsigned int[bmpWidth]; //初始化原始畫素的陣列。 if (biBitCount == 8) { for (int i = 0; i<bmpHeight / 2; i++) { for (int j = 0; j<bmpWidth / 2; i++) *(pBmpBuf + i*linebyte1 + j) = 0; } } if (biBitCount == 24) { for (int i = 0; i<bmpHeight; i++) { for (int j = 0; j<bmpWidth; j++) { for (int k = 0; k<3; k++)//每畫素RGB三個分量分別置0才變成黑色 { m = *(pBmpBuf + i*linebyte1 + j * 3 + k); count_xiang_su++; } n++; } } cout << "總的畫素個素為:" << n << endl; cout << "----------------------------------------------------" << endl; } if (biBitCount == 24) { for (int i = 0; i<bmpHeight; i++) { for (int j = 0; j<bmpWidth; j++) { out_r[bmpHeight - 1 - i][j] = pBmpBuf[j * 3 + 2 + bmpWidth*i * 3]; out_g[bmpHeight - 1 - i][j] = pBmpBuf[j * 3 + 1 + bmpWidth *i * 3]; out_b[bmpHeight - 1 - i][j] = pBmpBuf[j * 3 + bmpWidth *i * 3]; } } } //--------------------------------------------------------------------------------------- //將畫素資料存入TXT檔案。 ofstream outfile; char out_rgb[100] ; int ai,ji; outfile.open("rrbmp.txt", ios::in | ios::trunc); if(!outfile) cout << "error" << endl; for (ai = 0; ai<bmpHeight; ai++) { for (ji = 0; ji<bmpWidth; ji++) { int rgb_num = 16 + out_r[ai][ji]/43 + 6* (out_g[ai][ji]/43) + 36* (out_b[ai][ji]/43) ; sprintf(out_rgb,"%d,",rgb_num); outfile << out_rgb <<endl; } } outfile.close(); char writePath[] = "b.bmp"; saveBmp(writePath, pBmpBuf, bmpWidth, bmpHeight, biBitCount, pColorTable); //清除緩衝區 delete[]pBmpBuf; if (biBitCount == 8) delete[]pColorTable; } int main() { getRGB(); return 0; }
read.h
#include<fstream> #include<windows.h> #include<iostream> using namespace std; unsigned char *pBmpBuf;//讀入影象資料的指標 int bmpWidth;//影象的寬 int bmpHeight;//影象的高 RGBQUAD *pColorTable;//顏色表指標 int biBitCount;//影象型別,每畫素位數 //顯示點陣圖檔案頭資訊 void showBmpHead(BITMAPFILEHEADER pBmpHead){ cout << "\n點陣圖檔案頭:" << endl; cout << "檔案大小:" << pBmpHead.bfSize << endl; cout << "保留字_1:" << pBmpHead.bfReserved1 << endl; cout << "保留字_2:" << pBmpHead.bfReserved2 << endl; cout << "實際點陣圖資料的偏移位元組數:" << pBmpHead.bfOffBits << endl << endl; } //顯示點陣圖資訊頭資訊 void showBmpInforHead(BITMAPINFOHEADER pBmpInforHead){ cout << "\n點陣圖資訊頭:" << endl; cout << "結構體的長度:" << pBmpInforHead.biSize << endl; cout << "點陣圖寬:" << pBmpInforHead.biWidth << endl; cout << "點陣圖高:" << pBmpInforHead.biHeight << endl; cout << "biPlanes平面數:" << pBmpInforHead.biPlanes << endl; cout << "biBitCount採用顏色位數:" << pBmpInforHead.biBitCount << endl; cout << "壓縮方式:" << pBmpInforHead.biCompression << endl; cout << "biSizeImage實際點陣圖資料佔用的位元組數:" << pBmpInforHead.biSizeImage << endl; cout << "X方向解析度:" << pBmpInforHead.biXPelsPerMeter << endl; cout << "Y方向解析度:" << pBmpInforHead.biYPelsPerMeter << endl; cout << "使用的顏色數:" << pBmpInforHead.biClrUsed << endl; cout << "重要顏色數:" << pBmpInforHead.biClrImportant << endl; } //給定一個影象點陣圖資料、寬、高、顏色表指標及每畫素所佔的位數等資訊,將其寫到指定檔案中 bool readBmp(char *bmpName) { FILE *fp = fopen(bmpName, "rb");//二進位制讀方式開啟指定的影象檔案 if (fp == 0) return 0; //跳過點陣圖檔案頭結構BITMAPFILEHEADER fseek(fp, sizeof(BITMAPFILEHEADER), 0); //定義點陣圖資訊頭結構變數,讀取點陣圖資訊頭進記憶體,存放在變數head中 BITMAPINFOHEADER infohead; fread(&infohead, sizeof(BITMAPINFOHEADER), 1, fp); //獲取影象寬、高、每畫素所佔位數等資訊 bmpWidth = infohead.biWidth; bmpHeight = infohead.biHeight; biBitCount = infohead.biBitCount;//定義變數,計算影象每行畫素所佔的位元組數(必須是4的倍數) showBmpInforHead(infohead);//顯示資訊頭 int lineByte = (bmpWidth * biBitCount / 8 + 3) / 4 * 4;//灰度影象有顏色表,且顏色表表項為256 if (biBitCount == 8) { //申請顏色表所需要的空間,讀顏色表進記憶體 pColorTable = new RGBQUAD[256]; fread(pColorTable, sizeof(RGBQUAD), 256, fp); } //申請點陣圖資料所需要的空間,讀點陣圖資料進記憶體 pBmpBuf = new unsigned char[lineByte * bmpHeight]; fread(pBmpBuf, 1, lineByte * bmpHeight, fp); fclose(fp);//關閉檔案 return 1;//讀取檔案成功 } //儲存圖片 bool saveBmp(char *bmpName, unsigned char *imgBuf, int width, int height, int biBitCount, RGBQUAD *pColorTable) { //如果點陣圖資料指標為0,則沒有資料傳入,函式返回 if (!imgBuf) return 0; //顏色表大小,以位元組為單位,灰度影象顏色表為1024位元組,彩色影象顏色表大小為0 int colorTablesize = 0; if (biBitCount == 8) colorTablesize = 1024; //待儲存影象資料每行位元組數為4的倍數 int lineByte = (width * biBitCount / 8 + 3) / 4 * 4; //以二進位制寫的方式開啟檔案 FILE *fp = fopen(bmpName, "wb"); if (fp == 0) return 0; //申請點陣圖檔案頭結構變數,填寫檔案頭資訊 BITMAPFILEHEADER fileHead; fileHead.bfType = 0x4D42;//bmp型別 //bfSize是影象檔案4個組成部分之和 fileHead.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + colorTablesize + lineByte*height; fileHead.bfReserved1 = 0; fileHead.bfReserved2 = 0; //bfOffBits是影象檔案前3個部分所需空間之和 fileHead.bfOffBits = 54 + colorTablesize; //寫檔案頭進檔案 fwrite(&fileHead, sizeof(BITMAPFILEHEADER), 1, fp); //申請點陣圖資訊頭結構變數,填寫資訊頭資訊 BITMAPINFOHEADER infohead; infohead.biBitCount = biBitCount; infohead.biClrImportant = 0; infohead.biClrUsed = 0; infohead.biCompression = 0; infohead.biHeight = height; infohead.biPlanes = 1; infohead.biSize = 40; infohead.biSizeImage = lineByte*height; infohead.biWidth = width; infohead.biXPelsPerMeter = 0; infohead.biYPelsPerMeter = 0; //寫點陣圖資訊頭進記憶體 fwrite(&infohead, sizeof(BITMAPINFOHEADER), 1, fp); //如果灰度影象,有顏色表,寫入檔案 if (biBitCount == 8) fwrite(pColorTable, sizeof(RGBQUAD), 256, fp); //寫點陣圖資料進檔案 fwrite(imgBuf, height*lineByte, 1, fp); //關閉檔案 fclose(fp); return 1; }
這樣就可以獲得一張圖片的rgb值了,部分如下:
16,
16,
16,
16,
52,
52,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
16,
52,
52
因為目前還沒有實現磁碟驅動,沒有檔案系統,我們只能把值硬編碼進系統中。
bmp.h
一個數萬項的unsigned char陣列,再上面程式中我們同樣可以得到圖片的長寬資訊,那麼下一步就是在系統中顯示圖片了。
#include <bmp.h> void to_printf_dijkstra(int sx,int sy){ // sx,sy是螢幕解析度 int x, y; int k = 0 ; for (y = 0; y < 115; y++) { //圖片寬100,高115個畫素 for (x = 0; x < 100; x++){ printf_(sx , bmp_b[k] , x+sx-100, y, x+sx-100, y); k++; } } }
只需要在一個命令響應中呼叫這個函式即可了,這就是效果了(真機測試也是同樣)。
不知道你有沒有考慮到這個問題,這個115x100的圖片有11500個畫素點,這是一張很小很小的圖片了,如果我們用一張正常一點的尺寸較大的圖片,比如500x400,那就是200000個畫素點了,這對沒有實現磁碟驅動,靠硬編碼進系統的antz壓力非常大,所以此處載入圖片雖然成功了,但實際上還遠不能入此,這只是一個思想,當我可以實現硬碟驅動和檔案系統之後,我們可以把上面bmp檔案的rgb讀取的程式直接放在核心中成為API,然後呼叫起來就方便很多了,對於那種超大圖片,可以靠這樣實現分步顯示。