作者共發了15篇帖子。 字體大小:較小 - 100% (默認)▼  內容轉換:不轉換▼
 
點擊 回復
578 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)
 

本帖信息

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