18. Bölüm
Cenker Özkurt
Amiga'da, C ve grafik iyi bir ikili oluşturuyor. İsterseniz bunu bir inceleyelim ve gerçekleştirelim. Bu ayki konumuz bu olsun ne dersiniz?
Programımız, vermiş olduğumuz bir yazı dizisini, ekranın belirli bir satırında, pixel pixel scroll yaparak kaydırmak olsun. Her programcının ilk demosu aşağı yukarı böyle başlar. İçinizde bunu denemek isteyenlerin olduğunu tahmin ediyorum. Ayrıca bu program, sizler için iyi bir grafik alıştırması olacak. Daha önce görmüş olduğunuz ve zevkle seyrettiğiniz intro ve demolardaki kayan yazıların basit bir örneği olan programımızı şöyle bir gözden geçirelim:
/* Amiga Dunyasi */
/* Program : Cenker Ozkurt */
#include <stdio.h>
#include <exec/types.h>
#include <graphics/gfxbase.h>
#include <intuition/intuitionbase.h>
struct NewScreen ns={
0,0,320,256,5,0,1,0,CUSTOMSCREEN,0,0,0,0 };
struct RastPort *rp;
struct Screen *scr;
struct GfxBase *GfxBase;
struct IntuitionBase *IntuitionBase;
int f,s;
UBYTE *m=0xbfe001;
char *string="Amiga Dunyasi'dan. Yazan: Cenker Ozkurt. ";
main()
{
IntuitionBase=(struct IntuitionBase *)OpenLibrary("intuition.library",0);
if(IntuitionBase==NULL) {
printf("intuition library error!\n");
return(0);
}
GfxBase=(struct GfxBase *)OpenLibrary("graphics.library",0);
if(GfxBase==NULL) {
printf("graphics library error!\n");
CloseLibrary(IntuitionBase);
return(0);
}
scr=(struct Screen *)OpenScreen(&ns);
if(scr==NULL) {
printf("screen error!\n");
CloseLibrary(GfxBase);
CloseLibrary(IntuitionBase);
return(0);
}
rp=&scr->RastPort; /* Screen rastport */
SetAPen(rp,1L); /* Kalem rengi */
while(1) { /* Sonsuz döngü */
for(f=0;f<strlen(string);f++) { /* Yazi dizisi kadar */
Move(rp,310L,100); /* Yazi koordinati */
Text(rp,string+f,1L); /* Bir harf yaziliyor */
for(s=0;s<9;s++) { /* 8 pixel sola kaydirma */
WaitTOF(); /* Ekran taramasini bekle */
/* 1 pixel sola kaydır */
ScrollRaster(rp,1,0,0,90,320,110);
WaitTOF();
/* Yazi satirini kopyala */
ClipBlit(rp,0,90,rp,0,150,320,20,0xc0);
/* Sol mouse tuşu kontrolü */
if(*m==188) goto quit;
}
}
}
quit:
CloseScreen(scr);
CloseLibrary(GfxBase);
CloseLibrary(IntuitionBase);
return(1);
}
Evet şimdi programımızı compile edelim. İlk satırlardaki #include komutları;
#include <stdio.h> #include <exec/types.h> #include <graphics/gfxbase.h> #include <intuition/intuitionbase.h>standart input/output, veri tipleri, grafik ve intuition macrolarını tanımlıyorlar.
struct NewScreen ns={
0,0,320,256,5,0,1,0,CUSTOMSCREEN,0,0,0,0 };
komutlarıyla, ekran yapı değişkenine, bizim ekranımızın özelliklerini veriyoruz. 320x256 ekran boyu, 5 bitplane sayısı, CUSTOMSCREEN Workbench ekranına bağımsız olduğumuzu belirtiyor.
struct RastPort *rp; struct Screen *scr; struct GfxBase *GfxBase; struct IntuitionBase *IntuitionBase;RastPort, ekran özelliklerini taşıyan önemli bir yapı değişkenimiz. Bu değişken vasıtasıyla, diğer grafik komutları, çalışacakları ekranı tanıyorlar.
Screen, ekranımızın bütün özelliklerini taşıyan yapı değişken adresi.
GfxBase ve IntuitionBase, grafik ve intuition library adresininin saklandığı değişkenleri oluşturuyor.
Daha sonra programda kullanacağımız diğer değişkenler tanımlanıyor. Burada string değişkeni, bizim yazı dizimizin saklandığı değişken oluyor.
main() ana fonksiyonu içerisinde library'ler açılıyor ve kontrol ediliyorlar. Eğer açılamazlarsa, hata mesajı ile programdan geri çıkılıyor.
Sonraki satırlarda ana programa giriyoruz. Burada içiçe üç tane döngü bulunuyor. Birinci while döngüsü, sonsuz döngüyü oluşturuyor. Sol mouse tuşunu kontrol ederek bu döngüden çıkıyoruz. İkinci döngü, yazılacak olan yazı dizisi kadar devam ediyor. Bu arada,
Move(rp,310L,100); /* Yazi koordinati */ Text(rp,string+f,1L); /* Bir harf yaziliyor */ile, ekranda vermiş olduğumuz pozisyona yazı karakterimizi yazıyoruz. Daha sonra üçüncü döngüde, sekiz pixel kadar sola kaydırma yapılıyor. Burada ise,
WaitTOF(); ScrollRaster(rp,1,0,0,90,320,110); WaitTOF(); ClipBlit(rp,0,90,rp,0,150,320,20,0xc0);satırları bulunuyor. Amiga'da, ekran satır taramasının tamamlanıp tamamlanmadığını kontrol eden bir komut mevcut, WaitTOF(). Bu komut, daha çok Blitter kullanan komut işlemlerinde gerekiyor. Aksi takdirde, mesela bizim programımızda scroll sırasında bir titreme oluşuyor. Çünkü tarama tamamlanmadan yapılan bir kaydırma işlemi, senkron bozukluğuna sebep olabiliyor. Bu nedenle bu tür komutların özellikle başına WaitTOF komutunu yerleştirmek bozuklukları engelleyebiliyor.
Bir sonraki satırdaki ScrollRaster, isminden de anlaşılaşacağı gibi, herhangi bir yöne, herhangi bir bloğu kaydırmaya yarıyor. Oldukça kullanışlı bir komut. Biz de zaten kullandık! Ayrıca bu komut, işlemlerini blitter yardımıyla yapıyor. Kullanımı şöyle:
struct RastPort *rastport; int dx,dy,minx,miny,maxx,maxy; ScrollRaster(rastport,dx,dy,minx,miny,maxx,maxy);Burada dx ve dy kaydırılacak olan yönün, pixel olarak kaydırma miktarını belirtiyor. dx pozitif bir sayı ise, sola, negatif ise sağa kaydırmaya sebep oluyor. dy pozitif bir sayı ise yukarı, negatif ise aşağı kaydırmayı sağlıyor. Aynı anda hem sağa ya da sola veya herhangi bir yönde scroll yapılabiliyor. Kullanımda dikkat edilecek konu, ekran rastport değişkeninin doğru verilmesi. Aksi taktirde programınız **crash** edebilir.
Evet, sıra geldi ClipBlit fonksiyonuna. Bu fonsiyon (ya da komut) Amiga'nın başlıca grafik komutlarından. Yaptığı işlem, hafızanın belirli bir bloğunu başka bir bloğa taşımak. Bu işlemi yaparken de blitter çipini kullanır. Eğer bu blok ekran olursa, yaptığımız işlemler de ekranda görülüyor. Amiga oyun ve demolarında bu tür blok taşımaları çok kullanılır. Mesela bir oyundaki arka plan resimleri bu fonksiyon ile ekrana getirilir ve kaydırılır. Şimdi bu komutun kullanımını inceleyelim:
ClipBlit(rastport1,x1,y1,rastport2,x2,y2,dx,dy,minterm);x1 ve y1, 1. bloğun başlangıcını; x2 ve y2, 2. bloğun başlangıcını belirtir. 1. blok kaynak, 2. blok ise hedeftir. dx ve dy ise taşınacak olan bloğun genişliğini belirtir, rastport1 ve rastport2 blokları temsil eder. rastport'lar aynı bloğu temsil edebilir. Bizim programımızda da böyle kullanılmıştır. Yani kaynak ve hedef aynı rastport'tan (bizim programımızdaki ekran'dan) alınmıştır.
minterm ise, yerleştirmenin mantığını gösterir. 0xc0 kaynak bloğu hedef bloğa aynen taşır, minterm OR, AND, XOR ya da INVERT olabilir, minterm'lerin başlıcaları şunlardır:
mode minterm (decimal) invert 48 and 128 or 224 xor 96 replace 192Biz programımızda replace modunu yani hex olarak 0xc0 (decimal olarak 192) minterm'ini kullandık.
Ana program kısmımız burada bitiyor. Daha sonra screen ve library'lerimiz kapanarak programdan çıkılıyor. Programı,
CC +L prog.cile compile;
LN prog.o -lc32ile link edin. prog yazarak çalıştırın. Hoşlanacağınızdan eminim. Şimdi de bu ayki yeni örneğimiz:
/* Amiga Dunyasi */
/* Program : Cenker Ozkurt */
#include <stdio.h>
#include <exec/memory.h>
#include <graphics/gfxbase.h>
#include <intuition/intuitionbase.h>
struct NewScreen ns={
0,0,320,200,2,0,1,0,CUSTOMSCREEN,0,0,0,0 };
struct RastPort *rp;
struct Screen *scr;
struct GfxBase *GfxBase;
struct IntuitionBase *IntuitionBase;
struct RastPort rpx;
struct BitMap bmx;
int x,y;
UBYTE *m=0xbfe001;
main()
{
IntuitionBase=(struct IntuitionBase *) OpenLibrary("intuition.library",0);
if(IntuitionBase==NULL) {
printf("intuition library error!\n");
return(0);
}
GfxBase=(struct GfxBase *) OpenLibrary("graphics.library",0);
if(GfxBase==NULL) {
printf("graphics library error!\n");
CloseLibrary(IntuitionBase);
return(0);
}
scr=(struct Screen *) OpenScreen(&ns);
if(scr==NULL) {
printf("screen error!\n");
CloseLibrary(GfxBase);
CloseLibrary(IntuitionBase);
return(0);
}
rp=&scr->RastPort;
InitBitMap(&bmx,2,64,20);
bmx.Planes[0]=AllocMem(160,MEMF_CHIP|MEMF_CLEAR);
if(bmx.Planes[0]==NULL) {
printf("memory ?\n");
CloseLibrary(GfxBase);
CloseLibrary(IntuitionBase);
return(0);
}
bmx.Planes[1]=AllocMem(160,MEMF_CHIP|MEMF_CLEAR);
if(bmx.Planes[0]==NULL) {
printf("memory ?\n");
FreeMem(bmx.Planes[0],160);
CloseLibrary(GfxBase);
CloseLibrary(IntuitionBase);
return(0);
}
InitRastPort(&rpx);
rpx.BitMap=&bmx;
DrawEllipse(&rpx,32,10,30,8);
for(x=0;x<320;x+=64) {
for(y=0;y<200;y+=20) {
ClipBlit(&rpx,0,0,rp,x,y,64,20,0xc0);
}
}
while(*m!=188);
FreeMem(bmx.Planes[0],160);
FreeMem(bmx.Planes[1],160);
CloseScreen(scr);
CloseLibrary(GfxBase);
CloseLibrary(IntuitionBase);
return(1);
}
Bu programda, 64x20 boyutlarında bir bitmap hafıza açıyoruz. Bu bitmap bir ekran ile aynı özellikleri taşıyor. Fakat ekran gibi görülmüyor. Normal bir ekrana çizer gibi, bu ekrana da herhangi bir grafik komutuyla çizim yapabiliyoruz. Bu yaptığımız çizimleri de ekrana getirerek görmemiz mümkün.
Bizim bu programımız da, aynı şekilde bu bitmap'e çizilen bir resmi ekrana yayıyor.
Programımızın diğer bölümleri önceki programımızda olduğu gibi. Bu nedenle farklı yönlerini açıklamaya başlıyorum,
struct RastPort rpx; struct BitMap bmx;64x20 boyutlarındaki hafıza bloğumuzun rastport ve bitmap değişkenlerini tanımlıyoruz. Burada dikkat ettiyseniz rpx ve bmx pointer olarak tanımlanmamış. Çünkü bu hafıza bloğumuz ilk defa bizim tarafımızdan tanımlanıyor. Bundan dolayı rastport ve bitmap yapıları da bizim tarafımızdan tanımlanmak zorunda. rpx ve bmx gerçek rastport ve bitmap özelliklerinde ve uzunluğunda bulunuyor. Bu nedenle değişkenleri bir komut içerisinde kullanırken, başlarına & işaretini koymalıyız.
InitBitMap(&bmx,2,64,20);Bu fonksiyon ile bitmap'imizi yaratıyoruz. bmx oluşturulacak olan bitmap'in kendisi, 2 bitplane sayısı yani 4 renk, 64x20 ise bitmap'in x ve y boyunu belirtiyor.
bmx.Planes[0]=AllocMem(160,MEMF_CHIP|MEMF_CLEAR);bmx.Planes[0], oluşturulan bitmap'in ilk plane bloğu. Bizim bu hafıza bloğunu kullandığımızı sisteme belirtmemiz ya da diğer bir deyişle ayırtmamız gerekiyor.
AllocMem(160,MEMF_CHIP|MEMF_CLEAR);AllocMem fonksiyonu, bu işlemi yapmamızı sağlıyor. Bu fonksiyon işledikten sonra ayrılan hafıza blok adresinin başlangıç adresiyle geri dönüyor. 160 değeri bize gereken boş hafıza byte sayısını belirtiyor. Bu sayıyı,
160 = 64 / 8 * 20ile hesaplıyoruz. 64, x genişliğidir; 8, x boyunca harcanan byte sayısını bulmaya yarar; 20 ise, y boyunu belirtiyor. Bildiğiniz gibi soldan sağa 8 pixellik bir nokta, hafızada 1 byte yer harcar.
AllocMem içerisindeki MEMF_CHIP macrosu, ayrılacak olan hafıza bloğunun chip memory'de ayrılacağını belirtiyor. Burada MEMF_CHIP ya da MEMF_FAST macrolarını kullanmak mümkün.
MEMF_CLEAR, blok ayrılırken bloğun temizlenmesini sağlıyor. MEMF_CHIP ile MEMF_CLEAR macrolarını aynı anda kullanmak için, MEMF_CHIP|MEMF_CLEAR yazıyoruz. Burada iki macro birbirleri ile OR yapılmış oluyor.
Blok ayırırken dikkat edilecek husus, eğer blok içerisinde grafik komutları kullanılacaksa, bloğun chip hafızada ayrılması gerekiyor. Bu nedenle biz de MEMF_CHIP|MEMF_CLEAR macrolarını kullandık. Fast hafızada ayrılan hafıza bloklarına grafik komutları ile erişemiyoruz.
Daha sonra hafızanın yeterli olup olmadığını kontrol ederek yetersizse memory? mesajıyla programdan geri çıkıyoruz. Bir hata oluşmazsa ikinci plane için de aynı işlemleri yapıyor ve bu bitmap için bir de rastport açıyoruz.
InitRastPort(&rpx);rpx, bu rastport'u belirtiyor.
rpx.BitMap=&bmx;ile, rpx'in kullanacağı bitmap'i gösteriyoruz.
DrawEllipse(&rpx,32,10,30,8);
for(x=0;x<320;x+=64) {
for(y=0;y<200;y+=20) {
ClipBlit(&rpx,0,0,rp,x,y,64,20,0xc0);
}
}
DrawEllipse fonksiyonu ile açtığımız bloğa bir elips çiziyoruz. Fonksiyon içerisinde rpx'in kullanımına dikkat edin. rpx'in başına & işaretini koymazsanız, program **crash** edebilir. Burada, 32x10 çizilecek olan elipsin merkezini, 30x8 ise yarıçaplarını belirtiyor. Artık bu komuttan sonra açmış olduğumuz hafıza bloğuna bir elips çizilir. x ve y döngüleri ile bu elipsleri ekrana yayarız.
ClipBlit(&rpx,0,0,rp,x,y,64,20,0xc0);Yayma veya taşıma işlemi bu satırda yapılıyor. &rpx elipsin bulunduğu yer, 0x0 nerden itibaren taşınacağı, rp ekran rastport'umuzu, x ve y ekrana yerleştirilecek olan yeri, 64x20 taşınacak olan genişliği, 0xc0 ise minterm'i belirtiyor.
Bu satırda ClipBlit yerine, aynı fonksiyonu yapabilecek başka bir komut da kullanmak mümkün. Göstereceğim bu fonksiyon aslında aynı işi yapıyor; fakat ilk bloğu seçerken rastport yerine bitmap'ten yararlanıyor. Kullanımı ise şöyle:
BltBitMapRastPort(&bmx,0,0,rp,x,y,64,20,0xc0);Dikkat ettiyseniz, rpx yerine bmx yapısını yerleştirdik. Çünkü bu parametrenin bitmap belirten bir parametre olması gerekiyor.
Son satırlarda ise,
FreeMem(bmx.Planes[0],160); FreeMem(bmx.Planes[1],160);komutlarıyla, açmış olduğumuz ya da sisteme belirtmiş olduğumuz hafıza bloklarını serbest bırakıyoruz. Eğer bu satırları koymazsak programdan geri çıkabiliriz. Fakat bu bloklar serbest bırakılmadığı için sonraki programlar çalışmayabilir. Evet ikinci örneğimizi de burada noktaladıktan sonra, şimdi gelelim çalıştırılmasına. Önce Aztec C v3.6a disketinizi hazırlayın. Programı bir editör ile yazdıktan sonra ram'a kaydedin. Program ismimiz prog.c olsun. Compile etmek için,
CC +L prog.clink etmek için,
LN prog.o -lc32ve çalıştırmak için de prog yazın. Ekranı elipslerin doldurduğunu göreceksiniz.
Bu aylık da bu kadar. Gelecek ay sizlere, C'de pratik olarak ekrana çizmiş olduğunuz bir resmin nasıl yükleneceğini göstereceğim. Ayrıca biraz da IFF (Interchange Format File) dosyaları hakkında bilgi vereceğim.
Gelecek ay buluşmak üzere, hepinize iyi çalışmalar dilerim.