plazma - amatör bilgisayar kültürü

SDL Kursu 1

Emirhan (Ragnor) Bayyurt

1. SDL Nedir?

SDL, Simple DirectMedia Layer yani basit direkmedya katmanıdır. Biraz motamot bir çeviri oldu. Anlaşılır bir ifade ile SDL ses, klavye, mouse, joystick, 3d donanımı ve 2d grafik çizimi için hazırlanmış platform bağımsız bir çokluortam (multimedia) kütüphanesidir. Mpeg oynatıcılarda, Emulatorlerde, birçok popüler oyunda ve Linux'a port edilen birçok windows oyununda kullanılmaktadır.

SDL Linux, Windows, BeOS, MacOS Classic, MacOS X, FreeBSD, OpenBSD, BSD/OS, Solaris, IRIX, ve QNX ortamlarını destekler. Ayrıca resmi olmasada kod açık olduğu için gönüllü çalışmalar sonucunda Windows CE, AmigaOS, Dreamcast, Atari, NetBSD, AIX, OSF/Tru64, RISC OS, ve SymbianOS üzerinde de çalışması sağlanmıştır.

SDL C dili ile yazılmış olmasına rağmen C++ ise doğal hali ile çalışmakta ve Ada, Eiffel, Java, Lua, ML, Perl, PHP, Pike, Python, ve Ruby gibi birçok dil içinde uyarlamaları bulunmaktadır.

2. Neden SDL?

Çünkü SDL öğrenmesi ve kullanması kolay ama karşılığında çok güçlü bir kütüphanedir. Grafik konusunda OpenGL ile beraber çok kolay bir şekilde çalışabilmesine ek olarak eksik kaldığı her alan içinde SDL'i destekleyen ek kütüphaneler bulunmaktadır. Ses ve müzik için SDL_mixer, resimler için SDL_image, true type font desteği için SDL_ttf, ek çizim komutları için SDL_gfx ve network için SDL_net bunların en bilinen, en çok kullanılanlarındandır.

Bütün bunların yanında SDL platformdan bağımsızdır. Yazdığınız kod bir başka platformda neredeyse hiçbir değişikliğe gerek duymadan aynen çalışabilmektedir.

3. SDL Kurulum

Bu konu aslında basit olmasına rağmen yeni başlayanları biraz zorlayabilir. Yinede elimden geldiğince her platform'da SDL'i nasıl kurup kullanmaya hazır hale getireceğimizi yazmaya çalışacağım.

3.1. Windows

Windows ortamında en yaygın olarak MS Visual Studio, Borland C++ Builder ve Dev-C++ IDE'leri kullanılmaktadır. Ama ben daha önce hiç C++ Builder kullanmadığım ve internettede C++ Builder için yazılmış bir kurulum yazısı bulamadığım için size tahmini bilgi verebileceğim, gerisi size kalmış. Yinede pek sorun yaşayacağınızı sanmıyorum.

3.1.1. Visual Studio

İlk olarak MS'in IDE'si Visual Studio ile başlayalım. Ben Dev-C++ kullandığım için bu IDE hakkında pek bilgim yok ama internette yayınlanmış ingilizce derslerden topladığım bilgi ile yardımcı olmaya çalışacağım.

  1. http://www.libsdl.org/download-1.2.php adresinden download bölümünden, Development Libraries başlığı altındaki Visual C++ için olan dosyayı (SDL-devel-1.2.8-VC6.zip gibi bir adı olması lazım) indirin.

  2. Sıkıştırılmış dosyayı açın. Zip dosyasının içinden çıkan dosyalar arasında iki tane önemli klasör var. Bunlar "include" ve "lib" klasörleri. "lib" klasörünün içinden çıkanları C:\Program Files\Microsoft Visual Studio\VC98\Lib (büyük ihtimalle sizin bilgisayarınızdada aynı klasördür ama eğer MS VC++'ı kurduğunuz yer farklı ise bu yoluda ona göre değiştirin.) klasörü altına kopyalayın. "include" klasörü içindeki dosyalarıda C:\Program Files\Microsoft Visual Studio\VC98\Include\SDL (diğeri ile aynı şekilde farklı bir klasör olabilir sizin ki, artık duruma göre değişikliği siz yaparsınız. Birde include'un altında SDL klasörü yok onu sizin yaratmanız gerek.) klasörü altına kopyalayın.

  3. Şimdi Visual C++'ı çalıştırın ve yeni bir proje yaratın. "Win32 Application" seçeneğini ve "empty project" seçeneğini seçin. File menüsündeki New seçeneği ile yeni bir c++ kaynak dosyası (c++ source file) yaratın ve adını main.cpp koyun.

  4. Daha sonra proje ayarları (project settings) (project->settings menüsü yolu ile) bölümüne gidin. LINK tabına tıklayın listelenmiş diğer lib dosyalarının altına sdl.lib ve sdlmain.lib dosyalarını ekleyin.

  5. Ardından yine proje ayarları bölümünde C/C++ tabına tıklayın. Drop-down menüden "Code Generation" seçeneğini seçin. Ardından da 'Use run-time library' drop-down menüsünden multithreaded DLL' seçeneğini seçin.

  6. Son birşey daha. Sıkıştırılmış dosyanın içinde son bir önemli dosya bulunmakta. SDL.DLL dosyası. Bu dosyayı alıp ister işletim sisteminizin system klasörüne ( win 95, 98, ME için c:\windows\system klasörü ya da Windows NT, 2000 and XP için c:\windows\system32 klasörü), ister uygulamanızın exe dosyasının çalışacağı klasöre kopyalayın. Eğer DLL dosyasını system klasörüne kopyalarsanız yapacağınız her sdl programı için dll dosyasını baştan baştan kopyalamanıza gerek kalmaz. Aksi takdirde her programın klasörüne koymanız gerekir. Ayrıca programlarınızı dağıtırken de bu dll dosyasını programınız ile vermeniz gerekiyor. Aksi halde yaptığınız program başka bilgisayarlarda çalışmaz.

MS Visual Studio ile işimiz bu kadar bütün bu adımları gerçekleştirdikten sonra MS VC SDL ile program geliştirmeye hazır hale geliyor.

3.1.2. Dev-Cpp

Dev-C++ için SDL yüklemenin iki yolu var. Birincisi http://www.devpaks.org sitesine gidip sdl için gerekli devpak dosyasını indirmek ve Dev-C++ içerisindeki Package Manager ile bu devpak dosyasını yüklemek. Bu sayede hem sdl kütüphanesi sorunsuzca bilgisayarınıza yüklenecek hem de elinizin altında hızla program yazmaya başlamanız için hazır bir sdl kodu olacak.

Ama ne yazık ki hazır sdl kodu içerisinde birkaç hata bulunmakta yine de bunları düzeltmek oldukça kolay.

Diğer yol ise yukarıdaki gibi geliştirme paketini indirip her dosyayı yerine kurmak. Bu yolu izlerken yapmamız gerekenler;

  1. http://www.libsdl.org/download-1.2.php adresinden download bölümünden Development Libraries başlığı altındaki MinGW32 için olan dosyayı (SDL-devel-1.2.8-mingw32.tar.gz gibi bir adı olması lazım) indirin.

  2. Sıkıştırılmış dosyayı açın. Zip dosyasının içinden çıkan dosyalar arasında iki tane önemli klasör var. Bunlar "include" ve "lib" klasörleri. "lib" klasörünün içinden çıkanları C:\Dev-Cpp\lib klasörü altına kopyalayın. "include" klasörü içindeki dosyalarıda C:\Dev-Cpp\include\SDL (include'un altında SDL klasörü yok onu sizin yaratmanız gerek.) klasörü altına kopyalayın.

  3. Sıkıştırılmış dosyanın içinde son bir önemli dosya bulunmakta. SDL.DLL dosyası. Bu dosyayı alıp ister işletim sisteminizin system klasörüne ( win 95, 98, ME için c:\windows\system klasörü ya da windows NT, 2000 and XP için c:\windows\system32 klasörü), ister uygulamanızın exe dosyasının çalışacağı klasöre kopyalayın. Eğer DLL dosyasını system klasörüne kopyalarsanız yapacağınız her sdl programı için dll dosyasını baştan baştan kopyalamanıza gerek kalmaz. Ayrıca SDL.DLL dosyasını c:\Dev-Cpp\dll klasörünün altına da kopyalayın.

  4. Dev-Cpp'ı çalıştırın. New Project butonu ile yeni bir proje açın. Eğer Devpak'ı yüklediyseniz proje şablonları arasında Multimedia tabı altında SDL için hazır bir şablon hazır bulunmakta. Verdiğim dosya ile bu şablondaki dosyayı değiştirdiyseniz bu şablon sorunsuzca çalışacaktır. Bu şablon üzerinden dilediğinizce SDL uygulamalarınızı geliştirebilirsiniz. Ama eğer Devpak ile değilde SDL'in sitesinden indirdiğiniz sıkıştırılmış dosyadan kurduysanız proje ayarlarını elle yapmalısınız. İlk olarak yeni bir proje açın (New Project). Ardından Project Browser'da çıkan projenizin adına sağ tuşla tıklayın ve Project Options'ı seçin. Burda ilk olarak Type bölümünden Win32 GUI seçeneğini seçin. Eğer Win32 Console'u seçerseniz SDL programınız her çalıştığında önce dos penceresi açılır. Ardından programınız çalışır. Devamında aynı pencerede Parameters tabını açın. Compiler başlığı altına -I"<INCLUDE>\SDL" -Dmain=SDL_main parametrelerini girin. Linker başlığı altına ise -lmingw32 -lSDLmain -lSDL parametrelerini girin.

Ayarlamalar bu kadar. İsterseniz şimdi projenizi kaydedin ve SDL projesine başlayacakken ayarları hazır olduğundan bu proje üzerinden başlayın. Geriye bir tek kod yazmanız kaldı. O da sonraki bölümlerde anlatılacak.

3.1.3. Mingw32

Dev-Cpp'ta kendi içerisinde Mingw32 derleyicisi kullandığı için aşağı yukarı aynı ayarlar geçerli. Uzun uzadıya anlatmayacağım. Yukarıdaki açıklamalar yardımı ile sorunsuzca ayarlamaları yapabilirsiniz diye düşünüyorum.

3.2. Linux

Linux'ta SDL yüklemenin en kolay yolu kullandığınız dağıtımın paket yöneticisinden SDL paketini seçip indirmenizdir. Bir diğer yolda www.libsdl.org adresinden kaynak kodlarını indirip sırayla "./configure", "make" ve "make install" komutlarını vermeniz yeterlidir. Kodunuzu SDL ile derlemek içinde kodun içine '#include "SDL.h"' satırını eklemeniz ve derleyici parametrelerine de '-I"usr/include/SDL"' ve '-lSDL' parametrelerini eklemeniz gerekemekte.

4. SDL'e giriş

SDL öğrenmesi oldukça kolay bir kütüphanedir. Bu nedenle hızla kod yazmaya girişeceğim. Kısa sürede öğreneceksiniz zaten. SDL, 8 alt sistemin bileşiminden oluşmaktadır. Bunlar Ses(Audio), CDROM, Olay yönetimi(Event Handling), Dosya G/Ç(File I/O), Joystick yönetimi(Joystick Handling), Çoklu Görev(Threading), Zamanlayıcı(Timers) ve Grafik(Video)' dur. Bu alt sistemleri kullanmak için ilk önce bu sistemleri çalıştırmanız gerekir. Bunun için iki komut bulunmaktadır. Bunlar SDL_Init ve SDL_InitSubSystem dir. SDL_Init bütün SDL kodlarından önce çalıştırılmalıdır. Bu komut SDL sistemini çalıştırmaya başlar. SDL_InitSubSystem ise çalışma anında istediğiniz alt sistemin çalışmasını sağlar.

Kullanımları şu şekildedir:

SDL_Init ( SDL_INIT_VIDEO );

Bu komutla SDL programı çalıştırılır ve SDL'in video alt sistemi aktif hale getirilir.

SDL_InitSubSystem ( SDL_INIT_AUDIO | SDL_INIT_TIMER );

Bu komutla çalışmakta olan SDL programında ses ve zamanlayıcı alt sistemleri aktif hale getirildi. | işareti ile aynı anda birden fazla SDL alt sistemini seçebiliriz. Bu yönetmi SDL_Init komutu ile de kullanabiliriz.

Alt sistem bayrakları(flag) listesi:

  1. SDL_INIT_TIMER - Zamanlayıcı -

  2. SDL_INIT_AUDIO - ses -

  3. SDL_INIT_VIDEO - grafik -

  4. SDL_INIT_CDROM - cdrom -

  5. SDL_INIT_JOYSTICK - joystick -

  6. SDL_INIT_EVERYTHING - Bütün sistemleri aktif hale getirir -

  7. SDL_INIT_NOPARACHUTE - SDL'in hata sinyallerini yakalamasını önler -

  8. SDL_INIT_EVENTTHREAD - çok görevlilik -

Alt sistemleri çalıştırmayı öğrendik ama ya çalışan sistemleri kapatmayı? Şimdi de çalıştırdığımız SDL programını ve alt sistemleri nasıl kapatacağımızı öğreneceğiz.

İlk komutumuz SDL_Quit. Bu komut SDL_Init komutunun yaptığı işin tam tersini yapar ve başlattığınız SDL programını kapatır. Bu programı kullanırken herhangi bir argüman girmenize gerek yoktur. Kullanışı:

SDL_Quit();

şeklindedir. Bu komut ile hali hazırda çalışan bütün SDL alt sistemleri ve SDL programı kapanır. Diğer komutumuz ise SDL_QuitSubSystem. Bu komut ile çalışmakta olan istediğimiz alt sistemi kapatabiliriz. Kullanımı:

SDL_QuitSubSystem ( SDL_INIT_TIMER );

şeklindedir. Örneğimizde zamanlayıcı alt sistemini kapattık ama SDL programımız çalışmaya devam etti.

Ayrıca SDL_WasInit fonksiyonu ile istediğiniz alt sistemin yüklü olup olmadığını kontrol edebilirsiniz. Bu fonksiyonunun yazılışı şöyledir:

if(SDL_WasInit(SDL_INIT_VIDEO)!=0)
    printf("Video alt sistemi yuklu.\n");
else
    printf("Video alt sistemi yuklu degil.\n");

Şimdi öğrendiklerimizle örnek bir SDL programı yazalım.

/* SDL header dosyası. Butun SDL programlari
buna ihtiyac duyar */
#include "SDL.h"  
#include <stdio.h>

int main() {
  printf("SDL programi başlatılıyor.\n");
    
  /* SDL programi baslatilip Video ve Ses 
  sistemleri aktif hale getiriliyor */
  if((SDL_Init(
        SDL_INIT_VIDEO
        |SDL_INIT_AUDIO)==-1)
        ) 
  { 
    fprintf(
      stderr,
      "SDL programi baslatilamadi: %s.\n", 
      SDL_GetError()
      );
    exit(-1);
  }

  fprintf(stdout,"SDL programi baslatilamadi.\n");

  fprintf(stdout,"SDL programi kapatiliyor.\n");
    
  /* SDL programi ve butun alt sistemleri 
  kapatiliyor */
  SDL_Quit();
    
  fprintf(stdout,"Kapatiliyor....\n");

  exit(0);
}

5. SDL ve Grafik

Sırada grafik komutlarını kullanmayı öğrenmek var. İlk olarak yapmamız gereken şey video alt sistemini aktif hale getirmek. Ardından bir yüzey (surface) tanımlamalı ve SDL_SetVideoMode komutu ile bu yüzeyi kullanarak istediğimiz çözünürlükte bir pencere yaratmamız gerekir.

İlk olarak yüzey terimini açıklamak istiyorum. SDL'de ekrana çizdirmek istediğiniz bilgiler bir yüzeyde saklanıır. Bu yüzeyler aslında önceden tanımlanmış yapılardır (struct). Bir yüzeyi şöyle tanımlayabiliriz:

SDL_Surface *screen;

İçeriği ise şöyledir:

typedef struct SDL_Surface {
  Uint32 flags; /* Salt okunur */
  SDL_PixelFormat *format;/* Salt okunur */
  int w, h; /* Salt okunur */
  Uint16 pitch; /* Salt okunur */
  void *pixels; /* oku-yaz */

  /* kirpma bilgisi */
  SDL_Rect clip_rect; /* Salt okunur */

  /* Referans sayacı -- yuzey 
  bosaltilirken kullanilir */
  int refcount; /* cogunlukla okunur */

  /* Bu yapı aynı zamanda burda gösterilmeyen 
  bazı özel alanlara sahiptir */
} SDL_Surface;

Gördüğünüz gibi SDL_Surface yapısı ile uğraşırken sadece pixels değişkenini kullanabilirsiniz. Bu değişkende yüzeyin her pixel'inin renk bilgisini taşıyor ve isterseniz her pixel'i teker teker değiştirebilirsiniz. Bu konuya ileride daha detaylı olarak bakacağız ama şimdi devam edelim.

Programın başında ekrana yansıtacağımız görüntüler için bir yüzey tanımlarız ve bu yüzey ile bir pencere açarız. Bunu SDL_SetVideoMode komutu ile yaparız. Kullanımı aşadığaki gibidir.

screen = SDL_SetVideoMode(
  640, 480, 8, SDL_SWSURFACE);

Bu komut ile screen yüzeyini ekrana yansıtacağımız ana yüzey olarak tanımlar ve 640'a 480 pixel çözünürlükte bir pencere yaratırız. Ekrandaki bit başına düşen pixel sayısı 8 olur. Ve SDL_SWSURFACE bayrağı ile screen yüzeyine ait verilerin sistem belleğinde tutulması sağlanır. Burada kullanmak için birçok farklı bayrak bulunmaktadır. SDL_SetVideoMode komutu ile kullanabileceğimiz bayrakların listesi:

  1. SDL_SWSURFACE -> yüzeye ait bilgilerin sistem belleğinde tutulmasını sağlar.

  2. SDL_HWSURFACE -> yüzeye ait bilgilerin ekran kartının belleğinde tutulmasını sağlar.

  3. SDL_ASYNCBLIT -> asenkron yüzey göstermeyi aktif hale getirir. Bu genellikle tek işlemcili makinalarda bit işlemeyi (blit - bit block transfer - bit bloğu değişimi) yavaşlatır ama SMP sistemlerde hız artışı sağlayabilir.

  4. SDL_ANYFORMAT -> Normalde eğer video yüzeyi kullanılamayacak bir ekran derinliği (bpp) isterse SDL gölge bir yüzey ile bunu emule eder. SDL_ANYFORMAT bayrağı ile SDL'in bunu yapması engellenir ve SDL'in yüzeyin derinliğini umursamadan onu kullanması sağlanır.

  5. SDL_HWPALETTE -> SDL'e ayrıcalıklı palet erişimi verir. Bu bayrak olmadan SDL_SetColors komutu ile istediğiniz renge herzaman ulaşamayabilirsiniz.

  6. SDL_DOUBLEBUF -> Çifte tamponlamayı etkin hale getirir. Sadece SDL_HWSURFACE bayrağı ile beraber kullanılabilir. SDL_Flip komutu tamponların içeriğini değiştirir ve ekranı tazeler. Eğer çifte tamponlama etkinleştirilmemişse SDL_Flip bütün ekran üzerine SDL_UpdateRect komutu uygulanmış gibi davranır.

  7. SDL_FULLSCREEN -> SDL tam ekran çalıştırmaya çalışıyor.

  8. SDL_OPENGL -> OpenGL render ekranı yaratır. SDL_GL_SetAttribute komutu ile OpenGL ayarlamalarına başlamadan önce bu bayrağın etkinleştirilmesi gerekir.

  9. SDL_OPENGLBLIT -> Üstteki gibidir ama aynı zamanda blitting (*yardım*) işlemlerine izin verir.

  10. SDL_RESIZABLE -> Boyutlandırılabilir bir pencere yaratır. Pencere boyutları değiştirildiği zaman SDL_VIDEORESIZE olayı tetiklenir ve SDL_SetVideoMode yeni boyut ile tekrar çağırılabilir.

  11. SDL_NOFRAME -> Mümkün ise çerçevesiz bir pencere yaratır. Tam ekran modu otomatik olarak bu bayrağı etkinleştirir.

Eğer istediğiniz ekran modunun uygun olup olmadığını öğrenmek istiyorsanız SDL_VideoModeOK fonksiyonunu kullanabilirsiniz. Yazılışı:

if (!SDL_VideoModeOK(
  640, 480, 16, SDL_HWSURFACE))
{
  printf("Ekran modu uygun degil.\n");
}
else
{
  printf("Ekran modu uygun.\n");
}

şeklindedir. Bunun dışında SDL_GetVideoInfo, SDL_GetVideoSurface, SDL_GetVideoDriverName ve SDL_ListModes gibi fonksiyonlar da bulunmakta ama şimdilik işin başında olduğunuz için işin başındaki sizlerin ihtiyaç duymadığı fonksiyonlar. Bunlara ileride detaylı değineceğiz ama şimdi sadece başka fonksiyonların da olduğunu bilin yeter.

Şu ana kadar öğrendiklerimizle bir SDL programını başlatıp SDL penceresini açabiliyoruz. Ama karşımızdaki simsiyah pencere oldukça sıkıcı değil mi? Hadi ortamı biraz renklendirelim. İlk olarak oldukça basit olduğu için bir BMP dosyayını okuyup ekrana yazdırmayı göstereceğim.

Bunun için ilk olarak okuyacağımız BMP dosyasının içeriğini saklayacağımız bir yüzey oluşturmalıyız. Yüzeyler SDL'de resim bilgisi saklanılacağı heryerde kullanılır.

SDL_Surface *image;

SDL'in kendi içerisinde BMP uzantılı dosyaları okuyup hafızaya alan hazır bir fonksiyonu bulunmakta. Adı SDL_LoadBMP . Kullanışı:

image=SDL_LoadBMP("c:\a.bmp");

şeklindedir. Bu sayede image adlı yüzeye a.bmp dosyasını yüklemiş bulunmaktayız. Şimdi sıra bu resmi ekrana çizdirmekte. Bunun içinse SDL_BlitSurface fonksiyonunu kullanacağız.

SDL_BlitSurface(image, NULL, screen, NULL);

Bu komut ile image yüzeyindeki resmi screen yüzeyine yani ekrandaki görüntünün saklanacağı yüzeye çizdiririz. NULL değer verilen parametrelerde çizdirilecek yüzeylerin boyutları ve koordinatları belirlenir. Eğer iki parametreye de NULL girersek image yüzeyinin tamamı screen yüzeyinin 0,0 noktasından başlayarak çizdirilir. Eğer resmin belirli bir kısmını çizdirmek istersek veya ekranda 0,0 noktasından başka bir noktaya koymak istersek ne yapacağız? SDL_Rect kullanarak.

SDL_Rect SDL içerisinde kare alan tanımlamak için kullanılan yapıdır. İçeriğinde sadece enine ve boyuna uzunluğu ile x,y düzlemlerindeki koordinatlarını saklayan değişkenler bulunur.

typedef struct{
  Sint16 x, y;
  Uint16 w, h;
} SDL_Rect;

x ve y koordinatları üst sol köşenin koordinatlarıdır. w ve h ise genişlik ve uzunluğudur. Oldukça basit ama niye böyle bir yapıya ihtiyacımız var diye düşünebilirsiniz. Bu yapıya ihtiyacımız var çünkü yüzeylerdeki resim alanları aslen dikdörtgen ve bunları kırpmak ya da belirli koordinatlara yerleştirmek isterseniz bu dikdörtgen yapısı oldukça kullanışlı oluyor. Yapmamız gereken şu:

SDL_Rect dikdortgen;

şeklinde tanımlamayılız. Eğer amacımız resmimini belirli bir koordinata koymak ise şu yönetmi kullanmalıyız:

SDL_Rect hedef;
hedef.x = x; // resmi koymak istediğimiz 
             // noktanın x koordinatı
hedef.y = y; // resmi koymak istediğimiz 
             // noktanın y koordinatı
SDL_BlitSurface(
  image, NULL, screen, &hedef);

Ama eğer amacımız resmin sadece bir bölümünü çizdirmek ise bu yönetmi kullanmalıyız:

SDL_Rect dortgen1,dortgen2;

dortgen1.x = x; // resmin ekran üzerine 
                // yerleştirileceği x noktası
dortgen1.y = y; // resmin ekran üzerine 
                // yerleştirileceği y noktası
dortgen1.w = w; // resmin ekran üzerine 
                // çizilecek genişliği
dortgen1.h = h; // resmin ekran üzerine 
                // çizilecek uzunluğu

dortgen2.x = x2; // x düzlemindeki başlangıç 
                 // noktası
dortgen2.y = y2; // y düzlemindeki başlangıç 
                 // noktası

SDL_BlitSurface(
  image, &dortgen2, screen, &dortgen1);

Peki ya ekrandaki görüntünün bir kısmını bir yüzeye aktarmak istersek? Onu da bu yöntem ile yapabilirsiniz:

SDL_Rect dortgen1,dortgen2;

dortgen1.x = x1; // ekrandan kopyalanacak 
                 // parçanın sol üst 
                 // noktasının x koordinatı
dortgen1.y = y1; // ekrandan kopyalanacak 
                 // parçanın sol üst 
                 // noktasının y koordinatı
dortgen1.w = x2; // ekrandan kopyalanacak 
                 // parçanın sağ alt 
                 // noktasının x koordinatı
dortgen1.h = y2; // ekrandan kopyalanacak 
                 // parçanın sağ alt 
                 // noktasının y koordinatı

dortgen2.x = x1; // yukarıdakinin aynısı
dortgen2.y = y1; // yukarıdakinin aynısı

SDL_BlitSurface(screen, &dortgen2, 
  temp, &dortgen1);

Bunun dışında SDL_Rect kullanarak ekrana bir dikdörtgen çizdirmenizde mümkün. Bunun için bir SDL_Rect tanımlıyorsunuz. Bu dörtgeni yerleştireceğiniz x ve y koordinatlarını, dörtgenin genişliği ile uzunluğunu ve rengini belirledikten sonra SDL_FillRect fonksiyonu ile ekrana istediğiniz koordinata istediğiniz boyutlarda ve istediğiniz renkte bir dörtgen çiziyor. Kod şöyle :

Uint32 renk; // dörtgenimizin renk değeri
SDL_Rect dortgen; 
dortgen.x = x; // dörtgeni ekran üzerinde 
               // yerleştireceğimiz x noktası
dortgen.y = y; // dörtgeni ekran üzerinde 
               // yerleştireceğimiz y noktası
dortgen.w = w; // dörtgenimizin genişliği
dortgen.h = h; // dörtgenimizin uzunluğu
SDL_FillRect (screen, &dortgen, renk);

Oldukça kolay değil mi? Sanırım rengi nasıl belirttiğimizi merak ediyorsunuz. Ama şimdilik renk konularına girmeyeceğim ama ileride (az ilerde :)) detaylı olarak anlatacağım. Şimdi ise size ekran tazeleme fonksiyonlarını anlatacağım.

2D grafik programlamasında ekrana çizdireceğimiz görüntüleri önce çizdirmek sonra ekranı tazelemek ve sonra tekrar çizdirmek gerekir. Tazelemezsek ne olacağını basit bir örnek ile açıklayayım. Diyelim ki arkaplanda tam ekran çalışmakta olan bir penceredeki uygulamanız kilitlendi. Onun önündeki daha küçük bir pencerede çalışan uygulamanızın penceresini taşırsanır fark edeceğiniz üzere küçük pencerenin eski bulunduğu yerde görüntüsü (en azından görüntüsünün bir kısmı) hala durmakta. İşte ekrana çizim yaptıktan sonra ekranı tazelemezsek bu ve buna benzer bir sonuç alırız.

Peki ekranı nasıl temizleyeceğiz? Bunun iki yolu bulunmakta. Birincisi SDL_UpdateRect fonksiyonu ile.

SDL_UpdateRect(screen, 0, 0, image->w, image->h);

Ekrandaki görüntüyü sakladığımız screen yüzeyinin 0,0 koordinatından ekrana çizdireceğimiz image yüzeyinin genişliği ve yüksekliği boyunca uzanan alanı tazele komutudur bu. Bunun yerine bütün çizim işlemini bitirince ekranın tamamını tazeleyecek bir SDL_UpdateRect komutu daha kullanışlı olabilir. Şöyle ki :

SDL_UpdateRect(screen, 0, 0, 0, 0);

Ekranı tazelemek için kullanabileceğimiz bir diğer yöntem ise SDL_Flip fonksiyonudur. Bu fonksiyon sadece Video modu seçilirken çifte tamponlama ( Double Buffering) bayrağı (SDL_DOUBLEBUF) seçilmiş ise kullanılabilir. Çünkü bu komut tamponların değişmesini sağlamak yolu ile ekranı tazeler. Eğer çifte tamponlama özelliğini kullanamıyorsanız bu komut yerine yukarıdaki bütün ekranı tazeleyen SDL_UpdateRect(screen,0,0,0,0); komutunu kullanın. Ama imkanınız varsa SDL_Flip'i seçmeye gayret edin. Kullanımı:

SDL_Flip(screen);

Bunun dışında birde SDL_UpdateRect fonksiyonunun SDL_UpdateRects adında birden fazla dörtgeni aynı anda tazeleyen farklı bir versiyonuda bulunmaktadır. Bu fonksiyonun kullanımı ise şöyledir:

SDL_UpdateRects(
  screen, dortgensayisi, *dortgenler);

Şimdi ise birkaç pixel fonksiyonu göreceğiz. Surface'lerin yapısını tanıtırken sadece pixels değişkeninin değiştirilebilir olduğu belirtilmişti. Bu değişkende yüzeyin pixellerinin renk bilgisi saklanmaktadır. Bu değişken dizisinin değerleri değiştirilebilir ve bu değişiklikler sayesinde ekrandaki pixellerin rengi değiştirilir. Bu işi yapan basit iki pixel fonksiyonu yazalım. Biri seçtiğimiz yüzeye istediğimiz renkte bir pixel yerleştirmeye diğeride seçtiğimiz yüzeydeki istediğimiz pixel'in renk değerini almamıza yarayacak.

Uint32 getpixel(
  SDL_Surface *surface, int x, int y) 
{
  int bpp = surface->format->BytesPerPixel;
  /* p renk değerini almak 
  istediğimiz pixel'in adresi */
  Uint8 *p = (Uint8 *)surface->pixels 
    + y * surface->pitch + x * bpp; 
  switch(bpp) 
  {
    case 1:
      return *p;
    case 2:
      return *(Uint16 *)p;
    case 3:
      if(SDL_BYTEORDER == SDL_BIG_ENDIAN)
        return p[0] << 16 | p[1] << 8 | p[2];
      else
        return p[0] | p[1] << 8 | p[2] << 16;
    case 4:
      return *(Uint32 *)p;
    default:
      return 0; /* Bu sonuç çıkmaz ama 
                   ne olur ne olmaz. */
  }
}

Pixel dizisinde koordinatlar ilk 0,0 koordinatından başlar ilk elemanı ve her elemanda ilk olarak x değeri büyür. Bu büyüme pixel başına düşen bit kadar olur. Pixel başına 1 bit düşüyorsa her bit ayrı bir koordinattır. Ama 3 bit düşüyorsa 3 bitte bir koordinatları bir ileri gider. x koordinatı değeri limitine ulaştığında sıfırlanır ve y değeri bir artar.

Uint8 *p = (Uint8 *)surface->pixels 
  + y * surface->pitch + x * bpp; 

Satırında yaptığımız işlemde x değerini bpp değişkeni ile çarpıyoruz. bpp değişkeninde yüzeyin pixel başına düşen byte sayısını saklıyor. Yukarıda dizinin bir sonraki elemanının koordinat sisteminde ki bir sonraki noktanın rengini saklar demiştik ama eğer yüzeyde pixel başına düşen byte sayısı 1 ise doğrudur. Ama bazı durumlarda pixel başına düşen byte sayısı 2 veya 3'e çıkabilir. Mesela RGB renk paleti kullanıldığında her koordinat için ilk byte kırmızı renk değeri, ikincisi yeşil ve üçüncüsü mavi renk değeridir. RGBA renk paletinde ise ilk üçün RGB gibi dördüncüsü ise alfa değeridir. İşte bu yüzden x değerini bpp ile çarptık. Picth değişkeninde ise yüzeyin bir satırının uzunluğu tutuluyor. y değeri için bir demek x koordinatının limiti kadar gitmiş olmak ve bir nokta daha ileri gitmek demektir.

void putpixel(
  SDL_Surface *surface, int x, int y, Uint32 pixel)
{
  int bpp = surface->format->BytesPerPixel;
  /* p yerleştirmek istediğimiz pixel'in adresi */
  Uint8 *p = (Uint8 *)surface->pixels 
    + y * surface->pitch + x * bpp;

  switch(bpp) {
    case 1:
      *p = pixel;
      break;
    case 2:
      *(Uint16 *)p = pixel;
      break;
    case 3:
      if(SDL_BYTEORDER == SDL_BIG_ENDIAN) {
        p[0] = (pixel >> 16) & 0xff;
        p[1] = (pixel >> 8) & 0xff;
        p[2] = pixel & 0xff;
      } else {
        p[0] = pixel & 0xff;
        p[1] = (pixel >> 8) & 0xff;
        p[2] = (pixel >> 16) & 0xff;
      }
      break;
    case 4:
      *(Uint32 *)p = pixel;
      break;
    }
}

getpixel fonksiyonuna oldukça benziyor. getpixel fonksiyonunda hedef pixel'in adresini bulup ordaki değerleri alıyorduk, burda ise yine hedef pixel'in adresini buluyoruz, ardından hedef pixel'in değerlerini istediğimiz renk değeri ile değiştiriyoruz. Dikkat edilecek nokta ise renk değerini değiştirirken pixel başına düşen bit sayısına göre hesaplama yapıyoruz. Oldukça basit ama kullanışlı.

Bu arada bu pixel fonksiyonlarını kullanırken veya bir yüzeyin pixel verilerilerine direk ulaşırken ilk olarak üzerinde çalışacağınız yüzeyi kilitlemelisiniz. Açıkcası dalgınlık ve merak ile kilitlemeden de çalıştırdığım oldu. Bunun nedeni ise bütün yüzeylerin kilitlenmeye ihtiyacı olmamasıdır. Neden diye sormayın bilmiyorum. Ama öyle.

Peki bunu nasıl anlayacaksınız? SDL_MUSTLOCK fonksiyonu ile. En iyisi size bunu yapan bir kod ile açıklamak.

if ( SDL_MUSTLOCK(screen) ) {
  if ( SDL_LockSurface(screen) < 0 ) {
    fprintf(
      stderr, 
      "Yuzey kilitlenemiyor: %s\n", 
      SDL_GetError()
      );
    return;
  }
}

Burda ilk olarak screen yüzeyini SDL_MUSTLOCK fonksiyonu ile kontrol eder ve kilitlenmeyi gerektirip gerektirmediğine bakarız. 0 değeri dönerse istediğiniz zaman istediğiniz pixele veri yazabilir, istediğiniz pixelden veri okuyabilirsiniz. Ama 0 verisi dönmezse bu yüzeyi kilitlemeniz gerekir. Böyle olunca da SDL_LockSurface fonksiyonu devreye girer ve parametre olarak girilen yüzey kilitlenir ama bir sorun olur da kilitlenemezse fprintf fonksiyonu ile stderr dosyasına "Yüzey kilitlenemiyor: Hata mesajı" şeklinde bir hata bildirimi yazdırırız.

Bu arada SDL 1.1.8 'den beri yüzey kilitlemek rekürsif, yani ardarda istediğiniz kadar kilit atabilirsiniz bir yüzeye ama bu gibi durumlardada attığınız her kilit için yüzeyi bir kez daha açmanız gerekir.

Peki kilitlenen yüzeyi nasıl açacağız? SDL_UnLockSurface komutu ile. Kullanımı şöyledir:

SDL_UnlockSurface(screen);

Ama SDL_MUSTLOCK fonksiyonu ile beraber kullanmak istiyorsanız -ki tavsiye ederim- şöyle olacak:

if ( SDL_MUSTLOCK(screen) ) {
  SDL_UnlockSurface(screen);
} 

Bu kadar basit.

İlk ders için bu kadarı yeterli. Şu noktaya kadar anlattıklarım ile SDL programları yazmaya başlayabilirsiniz.

plazma - (2006 - 2011)