OpenGL之讀取Bitmap圖檔

OpenGL之讀取Bitmap圖檔

『Bitmap』圖檔之副檔名使用『.bmp』它非常簡單易讀,記得在2005年學DirextX時寫圖檔分析器就是它.缺點不支持壓縮.8Bit(256色)支持RLF壓縮但只有色彩單調時才有效果否則文檔更大. 不要以為256色已經淘汰,通過更換調色板的顏色可以快速更換顏色依然大有用處.渲染演示程式下載:

BMP文檔由四部分組成:

『Bitmap』文檔結構 簡介
FILE_HEADER    file; 圖檔的頭部
INFO_HEADER   info; 圖檔的信息
PALETTEENTRY  palette[256]; 調色板只用於256色.

16Bit、24Bit、32Bit均無調色板

PBYTE         buffer; 圖像數據

要在OpenGL中渲染像素『Pixel』要倒轉排成RGB/RGBA順序

 

FILE_HEADER文檔結構 簡介
WORD  type; ‘MB’ 0x4d42 BMP文檔標記

用於判斷是否BMP文檔

DWORD size; 文檔大小,判斷文檔是否完整
WORD  reserved1; 保留
WORD  reserved2; 保留
DWORD OffBits; 圖像數據的偏移量(文檔的頭部)

 

INFO_HEADER圖檔信息頭部 簡介
DWORD size; 圖檔信息頭部的大小
LONG  width; 圖檔寬度像素『Pixel』
LONG  height; 圖檔高度像素『Pixel』
WORD  planes; 平面量,總為1
WORD  bitCount; 位圖像素尺寸:

8Bit(256色)支持RLF壓縮

16BIT(分為RGB555與RGB565), 24BIT, 32BIT

DWORD compression; 壓縮類型:

0 = RGB

0 = RGB555 0x0RRRRRGGGGGBBBBB

3 = RGB565 0xRRRRRGGGGGGBBBBB

1 = RLE8 (run length encoding)壓縮

2 = RLE4

DWORD sizeImage; 圖檔數據所占空間,若使用RLE壓縮為壓縮後的大細
LONG  XPelsPerMeter; X軸每米像素
LONG  YPelsPerMeter; Y軸每米像素
DWORD ClrUsed; 圖像顏色量
DWORD ClrImportant; 圖像重要顏色量

 

PALETTEENTRY調色板 簡介
BYTE red; 紅色
BYTE green; 綠色
BYTE blue; 藍色
BYTE flags 只用於DirectDraw

PC_EXPLICIT:映射到硬件

PC_NOCOLLAPSE:不要映射

PC_RESERVED:保留

 

載入BMP位圖C代碼

bool Load_Bitmap(BITMAP_FILE_PTR bitmap,PBYTE data,int size)

{

int index;

int line_size;// 圖像每行所占的字節數

int pixel_size ;// 像素大小

PBYTE image;

int width ;// 圖寬

int height;// 圖高

// 讀取頭部數據

memcpy(&bitmap->header, data, sizeof(BITMAP_FILE_HEADER));

// 判斷是否是位圖文件

if (bitmap->header.type != BITMAP_ID)

return false;//出錯返回

// 讀取位圖信息的頭部

memcpy(&bitmap->info, data + sizeof(BITMAP_FILE_HEADER), sizeof(BITMAP_INFO_HEADER));

if (bitmap->info.sizeImage == 0)

{

bitmap->info.sizeImage = size – sizeof(BITMAP_FILE_HEADER) – sizeof(BITMAP_INFO_HEADER);

if (bitmap->info.bitCount == 8)

bitmap->info.sizeImage = bitmap->info.sizeImage – MAX_COLORS_PALETTE * sizeof(BITMAP_PALETTEENTRY);

}

//定位圖像數據

image = data + size – (int)bitmap->info.sizeImage;// 相對於文件尾

line_size = bitmap->info.sizeImage / bitmap->info.height;// 圖像每行所占的字節數

pixel_size = bitmap->info.bitCount / 8;// 像素大小

width = bitmap->info.width;// 圖寬

height = abs(bitmap->info.height);// 圖高

// 讀取位圖8或16,32位圖

if (bitmap->info.bitCount == 8)

{   // 讀取位圖的調色板

PBYTE palette = data + sizeof(BITMAP_FILE_HEADER) + sizeof(BITMAP_INFO_HEADER);

// RGBQUAD結構與PALETTEENTRY結構的順序調轉了

for (index = 0; index < MAX_COLORS_PALETTE; index++)

{//掉轉紅色和綠色

bitmap->palette[index].red   = palette[index * 4 + 2];

bitmap->palette[index].green = palette[index * 4 + 1];

bitmap->palette[index].blue  = palette[index * 4 + 0];

bitmap->palette[index].flags = PC_NOCOLLAPSE;

}

//根據位圖影像的大小生請空間

bitmap->buffer = (UCHAR *)malloc(abs(bitmap->info.width * bitmap->info.height * 3));

if (bitmap->buffer == NULL)

return false;//出錯返回

PBYTE buffer = NULL;

if (bitmap->info.compression == BITMAP_COMPRESSION_RLE8)

{

buffer = (PBYTE)malloc(abs(bitmap->info.width * bitmap->info.height));

Load_RLE8_Bitmap(buffer, image, bitmap->info.sizeImage, bitmap->info.width);// RLE解碼

image = buffer;

}

// 現在將索引值值轉為24BIT

for (int y = 0; y < height; ++y)

{

for (int x = 0; x < width; ++x)

{

index = y * width + x;

int color = image[index];

bitmap->buffer[index * 3 + 0] = bitmap->palette[color].red;

bitmap->buffer[index * 3 + 1] = bitmap->palette[color].green;

bitmap->buffer[index * 3 + 2] = bitmap->palette[color].blue;

}

}

if (bitmap->info.compression == BITMAP_COMPRESSION_RLE8)

free(buffer);

//最後將位圖位數變為24位

bitmap->info.bitCount = 24;

}

else

if (bitmap->info.bitCount == 16)// 讀取16位圖

{  // 以24BIT分配記憶體空間

bitmap->buffer = (UCHAR *)malloc(abs(bitmap->info.width * bitmap->info.height * 3));

if (bitmap->buffer == NULL)

return false;//出錯返回

if (bitmap->info.compression == 3)

{// RGB565

for (int y = 0; y < height; ++y)

{

for (int x = 0; x < width; ++x)

{// 現在將各個16位RGB值轉為32位值

index = y * line_size + x * 2;

WORD color = (image[index + 1] << 8) | image[index + 0];

UCHAR red = ((color >> 11) & 0x1f);

UCHAR green = ((color >> 5) & 0x3f);

UCHAR blue = (color & 0x1f);

index = y * width + x;

bitmap->buffer[index * 3 + 0] = (red << 3);

bitmap->buffer[index * 3 + 1] = (green << 2);

bitmap->buffer[index * 3 + 2] = (blue << 3);

}

}

}

else

{// RGB555

for (int y = 0; y < height; ++y)

{

for (int x = 0; x < width; ++x)

{

index = y * line_size + x * 2;

WORD color = (image[index + 1] << 8) | image[index + 0];

UCHAR red = (((color) >> 10) & 0x1f);

UCHAR green = (((color) >> 5) & 0x1f);

UCHAR blue = ((color) & 0x1f);

index = y * width + x;

bitmap->buffer[index * 3 + 0] = (red << 3);

bitmap->buffer[index * 3 + 1] = (green << 3);

bitmap->buffer[index * 3 + 2] = (blue << 3);

}

}

}

//最後將位16BIT變為24BIT

bitmap->info.bitCount = 24;

}

else

if (bitmap->info.bitCount == 24)// 讀取24BIT圖檔

{   // 根據位圖影像的大小申請空間

bitmap->buffer = (UCHAR *)malloc(bitmap->info.sizeImage);

if (bitmap->buffer == NULL)// 申請內存空間失敗

return false;//出錯返回

// 讀取圖像

for (int y = 0; y < height; ++y)

{

for (int x = 0; x < width; ++x)

{// 轉換GL_RGB模式

index = y * line_size + x * 3;

bitmap->buffer[index + 0] = image[index + 2];//逐個像素地拷貝

bitmap->buffer[index + 1] = image[index + 1];

bitmap->buffer[index + 2] = image[index + 0];

}

}

}

else

if (bitmap->info.bitCount == 32)// 處理32BIT圖檔

{       // 根據位圖影像的大小申請空間

bitmap->buffer = (UCHAR *)malloc(bitmap->info.sizeImage);

if (bitmap->buffer == NULL)//若不能申請空間

return false;//出錯退出

// 像素轉為BGRA 32Bit肯定是4字節對齊

for (index = 0; index < (int)bitmap->info.sizeImage-4; index=index+4)

{

bitmap->buffer[index + 0] = image[index + 2];//逐個像素地拷貝

bitmap->buffer[index + 1] = image[index + 1];

bitmap->buffer[index + 2] = image[index + 0];

bitmap->buffer[index + 3] = image[index + 3];

}

}

else

{

return false;//嚴重文提

}

if (bitmap->info.height < 0)// height為負時表示圖片顛倒

Flip_Bitmap(bitmap->buffer, bitmap->info.width*(bitmap->info.bitCount / 8), bitmap->info.height);

bitmap->info.height = abs(bitmap->info.height);

return true;

}

 

將顛倒的BMP文件翻轉過來

bool Flip_Bitmap(UCHAR *image, int bytes_per_line, int height)

{

UCHAR *buffer; //用於臨時保存位圖數據.

int index;     //循環計數

//分配單行空間

buffer = (UCHAR )malloc(bytes_per_lineheight);

if (buffer == NULL)

return false;

// 單行拷貝

memcpy(buffer, image, bytes_per_line*height);

// 垂直顛倒圖片

for (index = 0; index < height; index++)

memcpy(&image[((height – 1) – index)bytes_per_line], &buffer[indexbytes_per_line], bytes_per_line);

//釋放空間

free(buffer);

return true;//返回

}

評論