|
Double-Buffering Yöntemi ile Titremeyi(Flicker) Önlemek (C++.NET) |
|
Gönderiliyor lütfen bekleyin... |
|
|
Bu yazımda sizlere
Double Buffering yöntemini kullanarak titreme sorunundan nasıl
kurtulabileceğinizden bahsedeceğim.İlk olarak titreme sorununun oluşmasının
sebeblerinden bahsedelim isterseniz.MFC veya WinApi ile çizim işlemleri yapmış
arkadaşlarında bildiği gibi,çizimlerimizi herzaman WM_PAINT
mesajı içerisinde çizeriz.Bu işlemi gerçekleştirmemizin sebebi ise şudur.Bir
pencere içerisinde yapılan işlemlerin hiç biri windows tarafından saklanmaz.Yani
pencereyi yerleğiştirdiğimizde veya yenilediğimizde şekillerimizin silindiğini
görürüz.Şekillerin silinmemesinden programcı kendisi sorumludur.Pencere her
yenilendiğinde paint mesajı işlenir ve bu mesaj içerisinde
yapılmış işlemler ekrana yansıtılır ve diğer farklı dc nesneleri ile yaptığımız
çizimlerin hepsi silinir.
Görünüme her yeni eklediğimiz çizim işleminden sonra
çizimlerin aktif olabilmesi için yine bizim paint mesajını çağırmamız gerekir.Aksi
takdirde yaptığımız değişikliklerin hiç biri görünümde işlenmez. Eğer
bir şekli hareket ettiriyorsak veya görünümde bulunan çizimleri silmek istiyorsak
bunun için görünüm bölümünü boş küme haline getirip daha sonra paint
mesajını çağırmamız gerekir.Görünümü boş küme haline getirmemizin sebebi ise, paint
mesajı buna bakarak görünümü silip silmeyeceğine karar verir. Bu sayede görünümü
istediğimiz şekilde silebiliriz. Fakat bu nokta da bir sorun çıkmaktadır.
Bu işlemi üst üste tekrladığımızda, her defasında görünüm silindiği
için titreme meydana gelmektedir.Görünüme fazla çizim yaptığımızda ise bu sorun
çizim işlemlerini zorlaştırmakla birlikte, iyi bir görünüşte sunmamaktadır.
Paint mesajını
iki şekilde çağırabiliriz.UpdateWindow() ve InvalidateRect()
fonksiyonları ile.UpdateWindow fonksiyonu çağırdığımızda direk
olarak paint mesajını işler ve görünümün tamamını yeniler.Fakat InvalidateRect fonksiyonunun
kullanım şekli biraz daha farklıdır.Bu fonksiyonla görünümü istediğimiz
boyutlarda bir dikdörtgensel alan haline getirebiliriz.Fonksiyonun ilk parametresi
güncelleme alanı olarak belirlenecek bu dikdörtgensel bölgeyi belirtir.Bu parametreye
NULL değerini veririsek tüm görünüm bölümünü belirtmiş oluruz.İkinci
paremetresi ise paint mesajının zemini silip silmeyeceğine karar verir.False
değerini verirsek görünüme yapılan çizimleri eklenir fakat zemini silmez.Biz
bu yöntemde false değerini kullanacağız fakat zemin silinmediği için bir kaç
şey eklememiz gerekecek birazdan bundan bahsedeceğim.Aşağıda bu fonksiyonun
prototipini görebilirsiniz.
BOOL InvalidateRect(const RECT *lpRect, BOOL bErase); |
Eski çizimlerin silinebilmesi için bir buffer
kullanacağız.Her yeni çizimden sonra buffer i istediğimiz boyutta oluştutup,yeni
çizimlerimizi bu buffer da saklayacağız ve görünüme bunu yapıştıracağız.Zemini
silmediğimiz için hiç bir titreme olmayacak , eski çizimler ekranda duracak
fakat bizim için bu sorun olmayacak çünkü , biz her yeni çizimde buffer i
yeniden oluşturduğumuz için ve eski görünümün üzerine yapıştırdığımız için
sadece yeni çizdiğimiz şekiller görünümde çizilecek.Bu işlem windows tarafından
çok hızlı yapıldığı için bu esnada hiç bir titreme meydana gelmez.Bu yönteme
Double Buffering yöntemi adı verilir. Eğer tek bir buffer
değilde bunun yerine iki buffer kullanırsak bu yönteme de Triple
Buffering
adı verilir. Şimdi bunu biraz kodla anlatmaya çalışalım.
CDC dc //Ana dc değeri
CBitmap buffer; //buffer
CDC bufferdc; //Buffer dc nesnesi |
Görünüme çizim yapabilmemiz için
bir dc nesnesine ihtiyaç duyarız. Bu nesneyi CDC sınıfnı kullanarak
oluştururuz.
Bu sınıf içerisinde çizim fonksiyonları ve daha bir çok faydalı fonksiyonlar
bulunmaktadır.İlk olarak çizim yapmak için bir dc nesnesi oluşturduk.Bir alt
satırda çizimleri saklayacağımız, yukardada bahsettiğmiz bufferımızı oluşturduk
(buffer).Buffer için burda bir bitmap kullandık.Çizimlerimizi
bu bitmap üzerinde saklayabiliriz.Yine buffer üzerinde çizimleri yapabilmemiz
içinde bir dc nesnesine ihtiyacımız var ve nesnemizi burda oluşturduk (bufferdc).
CRect rc;
GetClientRect(&rc); //görünümün boyutlarını alıyoruz.
//bufferı yaratıyoruz.
bufferdc.CreateCompatibleDC(dc);
//görünümün boyutları ile aynı olacak bir bitmap olusturduk
buffer.CreateCompatibleBitmap(&bufferdc,rc.Width(),rc.Height()); //buffer arabiriminde bu bitmap i seçtik
bufferdc.SelectObject(&buffer); |
Yukarda da bahsettiğim
gibi eski şekillerin görünümden silinebilmesi için her yenilemede bufferı görünüme
yapıştırmamız gerekir.Bu sebebten dolayı buffer ın boyutları görünümün boyutları
ile aynı olmak zorundadır.Görünümün boyutlarını almak için CRect sınıfından
bir nesne türettik (rc).Sonrada GetClientRect fonksiyonu
ile görünümün boyularını bu türettiğimiz nesneye aldık.Buffer için oluşturduğumuz
nesneyi kullanabilmek için yukarda türettiğimiz ana dc ye bağlı olarak yarattık.Alt
satırda ise görünümün boyut değerleriyle eşit ve bufferdc ye bağlı olacak şekilde
bitmap ımızı yarattık.Sonrada bunu kullanabilmek ve üzerinde işlem yapabilmek
için bufferdc ye seçtik.
CBitmap image; //resmi okuyacağımız bitmap
BITMAP bitmap; //resmin özelliklerini tutan yapı
//dosyadan resmi okuyoruz.
image.m_hObject=(HBITMAP)LoadImage(theApp.m_hInstance,m_file,IMAGE_BITMAP,0,0,
LR_LOADFROMFILE|LR_DEFAULTSIZE);
image.GetObject(sizeof(bitmap),&bitmap); //resmin özelliklerini alıyoruz |
Burada görünüme
çizceğimiz resim için bir bitmap nesnesi türetiyoruz ve sonrada resmimizi bu
bitmap üzerine okuyoruz.Yalnız burda bir değişken daha kullandık (bitmap).Bu
değişkeni BITMAP yapısından türettik.Bu değişkeni, dosyadan
okuduğumuz resimin özelliklerini (boyutlarını,tipini vb..) öğrenmek için kullanacağız.
Resmin özelliklerini
elde etmek için CBitmap sınıfında bulunan GetObject fonksiyonunu
kullandık.Bu fonksiyona parametre olarak türettimiz BITMAP yapısından
türettiğimiz değişkenimizin adresini verdik.Fonksiyon içerisinde adresini aldığı
yapıya gerekli özellikleri yerleştirecektir.
CDC usedc; //çizim yapacağımız dc
usedc.CreateCompatibleDC(&bufferdc);//çizim yapacağımız dc yi yarattık
usedc.SelectObject(&image);//dosyadan okuduğumuz resmi seçtik |
Yukarıdaki kodta
ise dosyadan okuduğuz resmin üzerirnde işlem yapmak için bir dc değerine ihtiyacamız
olacağından yine CDC sınıfından bir nesne türettik.Bu nesneyi
yaratırken,resmi buffer üzerine çizeceğimiz için bufferdc ye bağlı olacak şekilde
yarattık.Son satırdada dosyadan okuduğumuz resmi yarattığımız dc nesnesine seçtik.
//dosyadan okuduğumuz resmi buffer a yapıştırdık
memdc.BitBlt(m_pt.x,m_pt.y,bitmap.bmWidth,bitmap.bmHeight,&usedc,0,0,SRCCOPY);
//bufferı da ekrana yapıştırdık
dc->BitBlt(0,0,rc.Width(),rc.Height(),&memdc,0,0,SRCCOPY); |
Yaptığımız bütün bu
işlemlerden sonra sıra bunları görünüme çizmeye geldi.Yukarıdaki kodda ilk olarak
resmimizi seçtiğimiz dc nesnesini buffera yapıştırıyoruz. Resmin boyutlarını
önceden BITMAP türünden olan değişkenimize atmıştık
ve burda bu değişkenimizden yararlanarak,resmin boyutlarına bağlı kalarak resmimizi
yapıştırdık.Son olarakta bufferı, görünüme çizim yapmak için en üstte tanımladığımız
dc nesnemize çizdik. Çizimlerimizin görülebilmesi için bu işlemlerin her yenilemede
yapılması gerekmektedir.
Siteden indirebileceğiniz,örnekte bu işlemin uygulanmış
şeklini görebilirsiniz. Programda bufferı açmak için menüden buffer seçeneğini
seçiniz.Bundan sonra titremenin yok olduğunu görebilirsiniz.İYİ
ÇALIŞMALAR.....
Örnek kaynak uygulamayı
indirmek için tıklayın.
Zeki Yuğnak
Erciyes Üniversitesi
Elektronik Mühendisliği
Makale:
Double-Buffering Yöntemi ile Titremeyi(Flicker) Önlemek (C++.NET) C++ ve C++.NET dili Zeki Yuğnak
|
|
|
-
-
Eklenen Son 10
-
Bu Konuda Geçmiş 10
Bu Konuda Yazılmış Yazılmış 10 Makale Yükleniyor
Son Eklenen 10 Makale Yükleniyor
Bu Konuda Yazılmış Geçmiş Makaleler Yükleniyor
|
|