目前共有15篇帖子。 字體大小:較小 - 100% (默認)▼  內容轉換:港澳繁體▼
 
點擊 回復
855 14
【講解】位圖的結構
一派掌門 二十級
1樓 發表于:2016-6-23 20:04
#include <stdio.h>
#include <Windows.h>

#pragma warning(disable: 4996)

int main(void)
{
    BITMAPFILEHEADER fileheader; // 位圖文件頭
    BITMAPINFOHEADER infoheader; // 位圖信息
    BYTE bits[100][452]; // 實際位圖數據, 第一維為高度, 第二維為寬度乘每像素的字節數, 第二維必須是4的倍數
    // 這裏位圖的寬度為150, 因為150*3=450, 450不是4的倍數, 比450大且離450最近的4的倍數是452, 所以第二維為452
    FILE *fp = fopen("image.bmp", "wb");
    if (fp == NULL)
        return 1;

    /* 寫入位圖文件頭信息 */
    fileheader.bfType = *(PWORD)"BM"; // 文件類型, 必須為BM
    fileheader.bfSize = sizeof(fileheader) + sizeof(infoheader) + sizeof(bits); // 位圖總大小
    fileheader.bfReserved1 = fileheader.bfReserved2 = 0; // 保留字節
    fileheader.bfOffBits = sizeof(fileheader) + sizeof(infoheader); // 文件開頭到位圖數據的距離
    fwrite(&fileheader, sizeof(fileheader), 1, fp);

    /* 寫入位圖信息 */
    infoheader.biSize = sizeof(infoheader); // 本結構體的大小
    infoheader.biWidth = 150; // 位圖的寬度
    infoheader.biHeight = 100; // 位圖的高度 (正數: 位圖數據從底到頂; 負數: 位圖數據從底到頂)
    infoheader.biPlanes = 1; // 目標設備的平面數 (必須為1)
    infoheader.biBitCount = 24; // 每個像素的顏色用24位, 即3位元組表示
    infoheader.biCompression = BI_RGB; // 壓縮方式
    infoheader.biSizeImage = 0; // 位圖數據的大小 (BI_RGB類型的位圖可設為0)
    infoheader.biXPelsPerMeter = infoheader.biYPelsPerMeter = 0; // 位圖水平、垂直解像度, 單位: 像素/米
    infoheader.biClrUsed = 0; // 位圖實際使用的顏色表中的顏色數, 0表示使用了所有顏色
    infoheader.biClrImportant = 0; // 為了顯示該位圖必須要用到的顏色數, 0表示所有顏色都用到了
    fwrite(&infoheader, sizeof(BITMAPINFOHEADER), 1, fp);

    /* 寫入圖像數據 */
    memset(bits, 0, sizeof(bits));
    bits[0][2] = 0xff;
    bits[0][4] = 0xff;
    bits[0][6] = 0xff;
    bits[1][0] = bits[1][1] = bits[1][2] = 0xee;
    fwrite(bits, sizeof(bits), 1, fp);
    
    fclose(fp);
    return 0;
}

所創建的位圖:

左下角:


一派掌門 二十級
2樓 發表于:2016-6-23 20:05
從程序中可以看出,一個(不使用調色板的)位圖文件的結構如下:
BITMAPFILEHEADER + BITMAPINFOHEADER + 實際位圖數據
 
一派掌門 二十級
3樓 發表于:2016-6-23 21:01
【一個使用調色板的3色位圖的例子】
#include <stdio.h>
#include <Windows.h>

#pragma warning(disable: 4996)

int main(void)
{
    BITMAPFILEHEADER fileheader;
    BITMAPINFOHEADER infoheader;
    BYTE bits[100][76]; // 150/2=75, 76%4=0
    RGBQUAD colors[] = {{201, 174, 254, 0}, {10, 166, 84, 0}, {1, 128, 255, 0}};
    FILE *fp;
    int i;
   
    fp = fopen("image.bmp", "wb");
    if (fp == NULL)
        return 1;

    fileheader.bfType = *(PWORD)"BM";
    fileheader.bfSize = sizeof(fileheader) + sizeof(infoheader) + sizeof(colors) + sizeof(bits);
    fileheader.bfReserved1 = fileheader.bfReserved2 = 0;
    fileheader.bfOffBits = sizeof(fileheader) + sizeof(infoheader) + sizeof(colors);
    fwrite(&fileheader, sizeof(fileheader), 1, fp);

    infoheader.biSize = sizeof(infoheader);
    infoheader.biWidth = 150;
    infoheader.biHeight = 100;
    infoheader.biPlanes = 1;
    infoheader.biBitCount = 4; // 1~16色
    infoheader.biCompression = BI_RGB;
    infoheader.biSizeImage = 0;
    infoheader.biXPelsPerMeter = infoheader.biYPelsPerMeter = 0;
    infoheader.biClrUsed = _countof(colors); // 顏色數
    infoheader.biClrImportant = 0;
    fwrite(&infoheader, sizeof(BITMAPINFOHEADER), 1, fp);

    fwrite(&colors, sizeof(colors), 1, fp);

    memset(bits, 0, sizeof(bits));
    for (i = 0; i < 30; i++)
        memset(bits[i], 0x11, 75);
    for (; i < 70; i++)
        memset(bits[i], 0x22, 75);


    fwrite(bits, sizeof(bits), 1, fp);
   
    fclose(fp);
    return 0;
}

所創建的位圖:

 
一派掌門 二十級
4樓 發表于:2016-6-23 21:12
【一個使用調色板的雙色位圖例子】
#include <stdio.h>
#include <Windows.h>

#pragma warning(disable: 4996)

int main(void)
{
    BITMAPFILEHEADER fileheader;
    BITMAPINFOHEADER infoheader;
    BYTE bits[100][20]; // 每字節表示8個像素, 每行只需19位元組, 20%4=0
    RGBQUAD colors[] = {{201, 174, 254, 0}, {10, 166, 84, 0}}; // 調色板中只有兩種顏色
    FILE *fp;
    int i;
   
    fp = fopen("image.bmp", "wb");
    if (fp == NULL)
        return 1;

    fileheader.bfType = *(PWORD)"BM";
    fileheader.bfSize = sizeof(fileheader) + sizeof(infoheader) + sizeof(colors) + sizeof(bits);
    fileheader.bfReserved1 = fileheader.bfReserved2 = 0;
    fileheader.bfOffBits = sizeof(fileheader) + sizeof(infoheader) + sizeof(colors);
    fwrite(&fileheader, sizeof(fileheader), 1, fp);

    infoheader.biSize = sizeof(infoheader);
    infoheader.biWidth = 150;
    infoheader.biHeight = 100;
    infoheader.biPlanes = 1;
    infoheader.biBitCount = 1; // 每一位表示一個像素
    infoheader.biCompression = BI_RGB;
    infoheader.biSizeImage = 0;
    infoheader.biXPelsPerMeter = infoheader.biYPelsPerMeter = 0;
    infoheader.biClrUsed = 0; // 2^biBitCount
    infoheader.biClrImportant = 0;
    fwrite(&infoheader, sizeof(BITMAPINFOHEADER), 1, fp);

    fwrite(&colors, sizeof(colors), 1, fp);

    memset(bits, 0, sizeof(bits));
    for (i = 0; i < 50; i++)
        memset(bits[i], 0xff, 10);
    for (; i < 100; i++)
    {
        memset(bits[i] + 10, 0xff, 8);
        bits[i][18] = 0xfc;
    }

    fwrite(bits, sizeof(bits), 1, fp);
   
    fclose(fp);
    return 0;
}
所創建的位圖:

 
一派掌門 二十級
5樓 發表于:2016-6-23 21:15
對於使用了調色板的位圖,其文件結構為:
BITMAPFILEHEADER + BITMAPINFOHEADER + RGBQUAD[n] + BYTE[a][b]
其中n為調色板中的顏色數
a為圖像高度
b為圖像寬度除以每字節的像素數,且b必須為4的倍數
 
一派掌門 二十級
6樓 發表于:2016-6-23 21:16
 
一派掌門 二十級
7樓 發表于:2016-6-23 21:23
另外,Windows里還有一個BITMAPINFO結構體,
BITMAPINFO = BITMAPINFOHEADER + RGBQUAD[1],
不過這裏用不到,我們只需要BITMAPINFOHEADER結構就行了
 
一派掌門 二十級
8樓 發表于:2016-6-23 21:27
BITMAPINFOHEADER結構體裏面的biSize成員非常重要,它標識了該結構體的版本。
歷史上已有很多種版本出現:

最常用的是大小為40的那個版本。
 
一派掌門 二十級
9樓 發表于:2016-6-23 22:11
biPlanes成員表示顏色平面數(Number of color planes),可用下面的代碼獲取當前顯示器的顏色平面數:
#include <stdio.h>
#include <Windows.h>

int main(void)
{
    HDC hdc = GetDC(NULL);
    int n;
    n = GetDeviceCaps(hdc, PLANES);
    ReleaseDC(NULL, hdc);
    printf("n=%d\n", n);
    return 0;
}
一般都是1。
 
一派掌門 二十級
10樓 發表于:2016-6-23 22:50
【創建位圖數據並放入剪切板】
#include <Windows.h>

#pragma warning(disable: 4996)

void main(void)
{
    BITMAPINFOHEADER infoheader;
    BYTE bits[100][20]; // 每字節表示8個像素, 每行只需19位元組, 20%4=0
    HGLOBAL hMem;
    LPBYTE lpMem;
    RGBQUAD colors[] = {{101, 174, 254, 0}, {221, 166, 84, 0}}; // 調色板中只有兩種顏色
    int i;

    infoheader.biSize = sizeof(infoheader);
    infoheader.biWidth = 150;
    infoheader.biHeight = 100;
    infoheader.biPlanes = 1;
    infoheader.biBitCount = 1; // 每一位表示一個像素
    infoheader.biCompression = BI_RGB;
    infoheader.biSizeImage = 0;
    infoheader.biXPelsPerMeter = infoheader.biYPelsPerMeter = 0;
    infoheader.biClrUsed = 0; // 2^biBitCount
    infoheader.biClrImportant = 0;

    memset(bits, 0, sizeof(bits));
    for (i = 0; i < 50; i++)
        memset(bits[i], 0xff, 10);
    for (; i < 100; i++)
    {
        memset(bits[i] + 10, 0xff, 8);
        bits[i][18] = 0xfc;
    }

    OpenClipboard(NULL);
    EmptyClipboard();

    hMem = GlobalAlloc(GMEM_MOVEABLE, sizeof(infoheader) + sizeof(colors) + sizeof(bits));
    lpMem = (LPBYTE)GlobalLock(hMem);
    memcpy(lpMem, &infoheader, sizeof(infoheader));
    memcpy(lpMem + sizeof(infoheader), colors, sizeof(colors));
    memcpy(lpMem + sizeof(infoheader) + sizeof(colors), bits, sizeof(bits));
    GlobalUnlock(hMem);
    SetClipboardData(CF_DIB, hMem);
   
    CloseClipboard();
}
 
一派掌門 二十級
11樓 發表于:2016-6-24 11:25
請看下面這句話:
int *a = (int *)malloc(2 * sizeof(int));
這句代碼動態分配了8位元組的內存,然後用指針變量p保存該內存塊的首地址。然而我們知道int *型指針只能操作4位元組的內存,也就是說*a只能訪問到該內存塊的前4個字節。
不過呢,指針可以進行加減運算,所以我們可以用*(a+1)來訪問後4個字節。當然a[1]也是可以的。
最後free(a)釋放的是整個8位元組的內存塊,而非sizeof(int)=4位元組。
 
一派掌門 二十級
12樓 發表于:2016-6-24 11:33
同樣的道理,雖然BITMAPINFO結構的第二個成員bmiColors數組只有一個元素,但是我們完全可以用下面的方法搞出多個元素來:
BITMAPINFO *p = (BITMAPINFO *)malloc(sizeof(BITMAPINFOHEADER) + n * sizeof(RGBQUAD));
其中n可以為任意自然數,包括0,表示調色板中的顏色數目。
然後我們就可以使用p->bmiColors[3]這樣大於0的下標了。
最後free(p)也是釋放整個內存空間,而不是只是sizeof(BITMAPINFO)。
 
一派掌門 二十級
13樓 發表于:2016-6-24 11:36
所以,BITMAPINFO這個結構體類型一般不拿來直接定義變量,而是常使用其指針類型。
另外,對於之前提到的int *a,我們也可以通過強制類型轉換來一次性訪問8個字節:
*(long long *)a
 
一派掌門 二十級
14樓 發表于:2016-6-24 13:54
位圖數據第二維長度的確定方法:使用<math.h>中的ceil函數。
#include <math.h>
#include <stdio.h>

int main(void)
{
    int i, n;
    for (i = 0; i < 24; i++)
    {
        n = (int)ceil(i / 4.0) * 4;
        printf("%d: %d\n", i, n);
    }
    return 0;
}
【輸出】
0: 0
1: 4
2: 4
3: 4
4: 4
5: 8
6: 8
7: 8
8: 8
9: 12
10: 12
11: 12
12: 12
13: 16
14: 16
15: 16
16: 16
17: 20
18: 20
19: 20
20: 20
21: 24
22: 24
23: 24
 
一派掌門 二十級
15樓 發表于:2016-6-24 14:33
 

回復帖子

內容:
用戶名: 您目前是匿名發表
驗證碼:
(快捷鍵:Ctrl+Enter)
 

本帖信息

點擊數:855 回複數:14
評論數: ?
作者:巨大八爪鱼
最後回復:巨大八爪鱼
最後回復時間:2016-6-24 14:33
 
©2010-2025 Purasbar Ver2.0
除非另有聲明,本站採用創用CC姓名標示-相同方式分享 3.0 Unported許可協議進行許可。