1. 程式人生 > >自制作業系統Antz——day13 顯示圖片

自制作業系統Antz——day13 顯示圖片

顯示圖片只是在多媒體課上看著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

unsignedchar

一個數萬項的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,然後呼叫起來就方便很多了,對於那種超大圖片,可以靠這樣實現分步顯示。