BMP Kütük Formatı ve Kullanımı
(1561 kelime) (449 okuma)
BMP Kütük Formatı
Bu dökümanda Windows BMP (bitmap) resim formatını inceleyeceğiz. Dökümanı
okuduktan sonra kolaylıkla .bmp uzantılı resim kütüklerini yükleyen ve kullanan
programlar yazabileceksiniz. Gerekli olan yerlerde örnek kod parçacıkları ile
anlatılan kavramların C/C++ kullanılarak gerçekleştirimlerini de açıklamaya
çalışacağım.
BMP Formatı Hakkında:
Bitmap (BMP) resim formatı Microsoft ve IBM işbirliği ile oluşturulmuş oldukça
basit bir formattır. Windows işletim sisteminde bu format standart olarak
desteklenmekte, resim, ikon ve pointer grafiklerini saklamak için kullanılmaktadır. Bu
resim formatının kısaca özelliklerini sıralamak gerekirse:
- Windows ve OS/2 işletim sistemlerince desteklenir.
- INTEL platformuna bağımlı özellikler içerir.
- 1,4,8,16,24 ve 32 bit lik renk çözünürlüklerine destek verir.
- 16 bitten düşük renk çözünürlükleri için palet yapıları kullanılır.
- Pixel verileri sıkıştırılmamış (genel kullanım) veya sıkıştırılmış olarak
saklanılabilir.
- Sıkıştırma kullanılıyor ise RLE algoritması olarak adlandırılan ve resim verisi
üzerinde kayıp olmasını engelleyen bir algoritma türü kullanılır.
Bu resim formatının bazı avantaj ve dezavantajları bulunmakta. İsterseniz
bunlarıda liste halinde görelim:
Avantajları:
- Kullanımı kolay, kütük yapısı anlaşılır (okuyan ve yazan programlar
geliştirmek oldukça basit.).
- Sıkıştırma algoritması resimde kayba yol açmıyor. (Örnegin JPEG formatı
sıkıştırma oranınca kayba uğratır.)
Dezavantajları:
- Çok yer kaplıyor. (sıkıştırma kullanan türevi bile...)
- INTEL platformuna bağımlı.
- Saydamlık verisi renk bilgisi içeriğinde yok (ALPHA kanalı bulunmuyor)
BMP Kütüklerinin Yapısı:
Aşağıdaki tabloda BMP kütükleri içerisinde yer alan veri bloklarının kütük
başlangıç adresleri (offset), uzunlukları, isimleri ve kısa açıklamaları
verilmiştir. Uzunluklar byte cinsinden, offset değerleri ise 16'lık sistemde
(hexadecimal), ve BMP kütüğünün başına görelidir. Byte ordering şekli INTEL
platformuna göredir. (Little Endian). Örneğin 32 bitlik bir sayıyı (4 byte)
gösterirken kütükten okunan ilk bayt sayının en düşük öncelikli byte değeri (0-7
nci bitler), okunan 4.byte ise en yüksek öncelikli byte değeridir.
offset |
uzunluk (byte) |
alan ismi |
kısa açıklama |
0000h |
2 |
identifier |
kütüğün bitmap kütüğü oldugunu belirten imza. BM karakterlerini
içerecek şekildedir. (0x42 , 0x4D) |
0002h |
4 |
file size |
bmp kütüğünün uzunluğunu taşır. |
0006h |
4 |
reserved |
kullanılmamakta |
000Ah |
4 |
bitmap data offset |
resim pixel verisi bloğunun kütük içerisindeki başlangıç adresini
içerir. |
000Eh |
4 |
bitmap header size |
bitmap info header isimli veri bloğunun uzunluğu bilgisini içerir. Bu
alan windows bmp formatı için herzaman 0x28 değerini taşır. |
0012h |
4 |
horizontal width |
resmin pixel olarak yatay genişliği |
0016h |
4 |
vertical height |
resmin pixel olarak düşey yüksekliği |
001Ah |
2 |
number of planes |
0x01 değerini içermeli |
001Ch |
2 |
bits per pixel (bpp) |
resimdeki her pixel için kaç bitlik renk verisi kullanıldığını
belirtir. Resmin desteklediği renk derinliği için farklı aşağıdaki değerleri
alabilir:
1: 2 renk
2: 4 renk
4: 16 renk
8: 256 renk
16: 216 renk
24: 224 renk
32: 232 renk |
001Eh |
4 |
compression method |
kullanılan sıkıştırma algoritmasını belirtir. Aşağıda belirtilen
değerleri alabilir:
00: sıkıştırma yok
01: RLE-8 bit
02: RLE-4 bit
03: Bitfields |
0022h |
4 |
bitmap data length |
resim verisinin uzunluğunu içerir. Uzunluk 4 ün katlarını alacak
şekilde gerekirse yuvarlanır. |
0026h |
4 |
horizontal resolution |
resmin genişliğini pixel/metre olarak verir |
002Ah |
4 |
vertical resolution |
resmin yüksekliğini pixel/metre olarak verir |
002Eh |
4 |
colors |
resim içinde kullanılan renk sayısını içerir. 256 renklik resimlerde
0x100 değerini içerir, 24-32 bitlik resimlerde 0x00 değerini içermektedir. |
0032h |
4 |
important colors |
önemsiz. |
0036h |
N x 4 |
palette |
resim palet verisini içerir. Palet verisi kullanan resimler için
gereklidir (16,24,32 bitlerde kullanilmaz). 4 byte lik bloklardan olusur. 1.byte mavi
(blue), 2.byte yesil (green) 3.byte kirmizi (red) 4. byte ise 0x00 olrak doldurulur. N
degeri 2,4,16,256 degerlerini (renk sayisi kadar..) alabilir. |
0436h |
kütük sonuna kadar... |
bitmap data |
resim verisi bu blokta bulunur. 16 bitlik resimler de her pixel için 2
byte kullanilir. ilk 5 bit blue, sonraki 5 bit green, sonraki 5 bit red renk verisi
içerir. son bit kullanilmaz. 24 bitlik resimlerde her renk komponenti 1 byte ile ifade
edilir. 32 bitlik resimlerde yine her renk komponenti 1 byte ile ifade edilir ama bu sefer
her pixel için 3 degil 4 byte kullanılır. Son byte her zaman 0x00 değerini içerir.
Resim verisi asağıdan yukarıya doğru çıkan satırlardan oluşur. Yani okunan ilk
değer resmin sol alt köşesinin renk verisidir. Okunan son değer ise resmin sağ üst
köşesi renk verisini içerir. |
BMP Kütüklerini Okumak:
Şimdi yukarıda verilen kütük yapısındaki bilgileri kullanarak .bmp kütüklerini
okuyan bir C programı yazacağız. Programın basit olması açısından sadece
sıkıştırılmamış ve 24 bitlik renk verisi içeren kütükleri okuyabileceğiz.
İlk olarak aşağıdaki yapıları tanımlayalım:
struct sBitmapHeader {
unsigned short id;
unsigned int fileSize;
unsigned int reserved;
unsigned int dataOffset;
unsigned int BmpInfoHeaderSize;
unsigned int width;
unsigned int height;
unsigned short planes;
unsigned short bpp;
unsigned int compMethod;
unsigned int dataLength;
unsigned int horzRes;
unsigned int vertRes;
unsigned int numColors;
unsigned int numImportantColors;
};
struct sPixel {
unsigned char b;
unsigned char g;
unsigned char r;
};
Bu yapıları tanımladıktan sonra artık programımızın bitmap yükleme
kısımlarını içeren rutinleri yazmaya başlayabiliriz. İlk olarak diskte yer alan
"ornek.bmp" isimli kütüğü açan ve bitmap başlik verisini (header) okuyan
kod parçasını yazalım:
struct sBitmapHeader header;
FILE *fp;
// kütüğü aç
fp = fopen("ornek.bmp","rb");
if (!fp) {
printf("kütük bulunamadı!");
exit(-1);
}
// header verisini oku
fread(&header,sizeof(header),1,fp);
Yukaridaki birkaç satırlık kod ile bitmap başlık verisini (header) okuduk. header
olarak tanımladığımız değişkenin ilgili alanlarına erişerek bmp kütüğü
hakkında birçok yararlı bilgiye ulaşabiliriz. Bizim için en önemli bilgiler width
(genislik), height (yükseklik) ve bpp (bits per pixel) isimli alanlardır. Aşağıdaki
kod ile öncelikle bmp kütüğünün bizim istediğimiz formatta veri içerip
içermediğine bakılıyor, daha sonra ise kütükte yer alan pixel verileri belleğe
okunuyor.
// kütüğün Windows BITMAP kütüğü olup olmadığını doğrula
if (header.id != 0x4D42) {
printf("kütük windows bitmap (bmp) kütügü degil!");
exit(-1);
}
// renk derinliği 24 bit olmalı
if (header.bpp != 24) {
printf("kütük renk derinliği 24 bit değil!");
exit(-1);
}
// sıkıştırılma kullanılmamıs olması gerekiyor
if (header.compMethod != 0) {
printf("kütük sıkıştırılmış veri içeriyor!");
exit(-1);
}
// pixel veri bloğunun başlangıcına gidelim
fseek(fp,header.dataOffset,SEEK_SET);
// tüm pixel verilerini alacak büyüklükte bir bellek alani olusturup
// daha sonra diskten bu alana pixel verilerini okuyoruz
sPixel *data = new sPixel[header.width * header.height];
fread(data,sizeof(sPixel),header.width*header.height,fp);
// artik kütük ile isimiz bitti
fclose(fp);
Evet işte bu kadar basit. Artık elimizde data dizisi içerisinde resmi oluşturan her
pixelin renk değerleri bulunuyor. Bu değerlere örneğin data[i].r, data[i].g, data[i].b
şeklinde erişebilirsiniz. Değiştirdiğiniz değerleri daha sonra tekrar kütüğe
yazabilirsiniz.
OpenGL İle Birlikte Kullanımı:
Son olarak bu resim verisini OpenGL programlarında texture olarak kullanmak
istiyorsanız aşağıdaki örnek kod parçacığını kullanmanız yeterlidir.
// OpenGL r,g,b şeklinde dizilim ister. Bizdeki ise b,g,r şeklinde
// Bu ufak rutin ile çevirim işlemi yapıyoruz.
for (int i=0;i<header.width*header.height;++i) {
unsigned char tmp = data[i].r;
data[i].r = data[i].b;
data[i].b = tmp;
}
int textureId;
glGenTextures(1,&textureId);
glBindTexture(GL_TEXTURE_2D,textureId);
glTexEnvi(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_DECAL);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
gluBuild2DMipmaps(GL_TEXTURE_2D,3,header.width,header.height,GL_RGB,GL_UNSIGNED_BYTE,data);
Herzaman olduğu gibi bu döküman hakkındaki fikirlerinizi, varsa hataları ve
düzeltme isteklerinizi sitemiz forumları aracılığı ile veya deniz@oyunyapimi.org adresi üzerinden direk olarak
benimle bağlantıya geçerek bildirebilirsiniz.
M.Deniz Aydınoğlu :: 2003 :: www.oyunyapimi.org
|