Zmniejsz migotanie dzięki GDI + i C++

Używam GDI+ w aplikacji C++ / MFC i po prostu nie mogę uniknąć migotania, gdy okno jest zmieniane.

Próbowałem już tych kroków:

  • return TRUE on OnEraseBkGnd();
  • Return NULL on OnCtlColor();
  • używane podwójne buforowanie zgodnie z tym kodem:

void vwView::OnDraw(CDC* pDC) 
{
   CRect rcClient;
   GetClientRect(rcClient);

   Bitmap bmp(rcClient.Width(), rcClient.Height());
   Graphics graphics(&bmp);

   graphics.DrawImage(m_image, rcClient.left, rcClient.top);

   Graphics grph(pDC->m_hDC);
   grph.DrawImage(&bmp, 0, 0);
}
Czy robię coś nie tak? A może jest inny sposób, aby to osiągnąć?
Author: Shog9, 2008-10-13

6 answers

Aby całkowicie uniknąć migotania, należy wykonać Wszystkie rysunki w odstępie między aktualizacjami ekranu. Windows nie zapewnia żadnych łatwych sposobów osiągnięcia tego dla normalnego malowania okien (Vista zapewnia rysowanie kompozytowe za pomocą DWM , ale nie można na tym polegać nawet na systemach z systemem Vista). Dlatego najlepsze, co możesz zrobić, aby zminimalizować migotanie, to narysować wszystko tak szybko, jak to możliwe (reduce łzawienie, zwiększając swoje szanse na ukończenie wszystkich rysowanie w cyklu odświeżania) i unikanie nadmiernego rysowania (rysowanie części ekranu, a następnie rysowanie czegoś innego na górze: ryzyko przedstawienia Użytkownikowi częściowo narysowanego ekranu).

Omówmy techniki przedstawione tutaj do tej pory:

  • Do-nothing OnEraseBkgnd(): pomaga uniknąć nadmiernego rysowania, zapobiegając wypełnieniu unieważnionego obszaru okna kolorem tła okna. Przydatne podczas rysowania całego obszaru ponownie podczas WM_PAINT obsługa w każdym razie , jak w przypadku rysunku podwójnie buforowanego... ale zobacz uwagi na unikanie overdraw poprzez zapobieganie rysowaniu po WM_PAINT metoda .

  • Zwracanie NULL dla OnCtlColor(): to nie powinno nic robić ... chyba, że masz kontrolę nad dzieckiem na formularzu. W takim przypadku, zobacz uwagi na unikanie overdraw poprzez zapobieganie rysowaniu po WM_PAINT metoda zamiast.

  • Podwójny buforowany rysunek : pomaga uniknąć rozdarcia (i potencjalnie nadmiernego rysowania), zmniejszając rzeczywisty rysunek na ekranie do pojedynczego BitBLT . Może to zaszkodzić czasowi potrzebnemu do rysowania: nie można użyć akceleracji sprzętowej (chociaż w przypadku GDI+ szanse na użycie rysowania wspomaganego sprzętowo są dość niewielkie), należy utworzyć i wypełnić bitmapę off-screen dla każdego przerysowania, a całe okno musi być przemalowane każde przerysowanie. Patrz uwagi na temat efektywnego podwójnego buforowania .

  • Używanie wywołań GDI zamiast GDI+ dla BitBlt : często jest to dobry pomysł - Graphics::DrawImage() może być bardzo wolne. Znalazłem nawet normalne GDI BitBlt() zadzwoń, aby być szybszym na niektórych systemach. Pobaw się tym, ale dopiero po wypróbowaniu kilku innych sugestii.

  • Unikanie stylów klasy okien, które wymuszają pełne przerysowanie przy każdej zmianie rozmiaru (CS_VREDRAW, CS_HREDRAW): To pomoże, ale tylko wtedy, gdy nie trzeba przerysować całego okna po zmianie rozmiaru.

Uwagi na unikanie overdraw poprzez zapobieganie rysowaniu przed WM_PAINT metoda

Gdy całość lub część okna zostanie unieważniona, zostanie ono usunięte i przemalowane. Jak już wspomniano, możesz pominąć kasowanie, jeśli planujesz przemalować cały nieprawidłowy obszar. jednakże, jeśli pracujesz z oknem potomnym, to należy upewnić się,że okna nadrzędne nie usuwają również obszaru ekranu. Styl WS_CLIPCHILDREN powinien być ustawiony we wszystkich oknach nadrzędnych - zapobiegnie to rysowaniu obszarów zajmowanych przez okna podrzędne (w tym widoku).

Uwagi na unikanie overdraw poprzez zapobieganie rysowaniu po WM_PAINT metoda

Jeśli masz jakiekolwiek kontrolki dla dzieci umieszczone w formularzu, będziesz chciał użyć stylu WS_CLIPCHILDREN , aby unikaj rysowania nad nimi (a następnie bycia przez nich narysowanym. Należy pamiętać, że wpłynie to nieco na szybkość procedury BitBlt.

Uwagi na temat efektywnego podwójnego buforowania

W tej chwili tworzysz nowy obraz bufora tylnego za każdym razem, gdy Widok rysuje się sam. W przypadku większych okien może to oznaczać znaczną ilość przydzielanej i zwalnianej pamięci, a spowoduje poważne problemy z wydajnością. Polecam trzymanie dynamicznie przydzielanej bitmapy w obiekt widoku, realokując go w razie potrzeby, aby dopasować rozmiar widoku.

Zauważ, że podczas zmiany rozmiaru okna, spowoduje to tyle samo alokacji co obecny system, ponieważ każdy nowy rozmiar będzie wymagał przydzielenia nowej bitmapy bufora tylnego, aby ją dopasować - możesz nieco złagodzić ból, zaokrąglając wymiary do następnej największej wielokrotności 4, 8, 16 itd., co pozwala uniknąć ponownego przydzielania na każdą drobną zmianę rozmiaru.

Zauważ, że jeśli wielkość okno nie zmieniło się od czasu ostatniego renderowania do tylnego bufora, nie musisz go ponownie renderować, gdy okno jest unieważnione - po prostu wyrzuć już renderowany obraz na ekran.

Przydziel również bitmapę, która odpowiada głębi bitowej ekranu. Konstruktor Bitmap, którego aktualnie używasz, będzie domyślnie ustawiony na 32BPP, ARGB-layout; jeśli nie pasuje do ekranu, będzie musiał zostać przekonwertowany. Rozważ zastosowanie metody GDI CreateCompatibleBitmap() aby uzyskać dopasowanie bitmapy.

W końcu... Zakładam, że Twój przykładowy kod jest tylko tym, ilustracyjnym fragmentem. Ale jeśli w rzeczywistości nie robisz nic poza renderowaniem istniejącego obrazu na ekranie, nie musisz w ogóle utrzymywać bufora tylnego - po prostu Blt bezpośrednio z obrazu (i przekonwertuj format obrazu z wyprzedzeniem, aby dopasować go do ekranu).
 39
Author: Shog9,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-06-11 20:20:10

Możesz spróbować użyć staroświeckiego GDI zamiast GDI+ do pisania do DC, zwłaszcza, że już buforujesz obraz. Użyj Bitmap:: LockBits, aby uzyskać dostęp do surowych danych bitmap, Utwórz strukturę BITMAPINFO i użyj SetDIBitsToDevice, aby wyświetlić bitmapę.

 4
Author: Mark Ransom,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-13 15:53:24

Upewnij się, że Klasa okna nie zawiera znaczników CS_VREDRAW i CS_HREDRAW w swoim stylu.

Zobacz http://msdn.microsoft.com/en-us/library/ms633574 (VS.85). aspx

 3
Author: Frederik Slijkerman,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-13 16:30:20

Możesz uzyskać trakcję dzięki użyciu Direct3D, aby "powiedzieć" ci, kiedy wystąpi vsync, i in., dzięki czemu możesz BitBlt / update w odpowiednim czasie. Zobacz też GDI vsync, aby uniknąć rozdarcia (chociaż sprowadzanie rzeczy do jednego małego BitBlt może być "wystarczająco dobre" w niektórych przypadkach).

Należy również zauważyć, że wygląda na to, że GDI BitBlt nie jest zsynchronizowany z screen vsync. Zobacz Szybszy niż BitBlt.

Należy również zauważyć, że za pomocą CAPTUREBLT (który pozwala na przechwytywanie przezroczystych okien) powoduje migotanie myszy (jeśli aero nie jest używany), jeśli jest używany.

 3
Author: rogerdpack,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-10-31 17:53:49

Ten link zawiera kilka przydatnych informacji: http://www.catch22.net/tuts/flicker-free-drawing

(wiem, że jest to bardzo późny dodatek do wątku, ale jest to dla każdego (takiego jak ja), który znalazł go, gdy chciał zmniejszyć migotanie w mojej aplikacji Win32...)

 2
Author: Liam,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-08-30 09:55:20

Czy w formularzu są okna potomne? Menedżer okien zaczyna od wymazania tła przez okno nadrzędne, wysyłając wiadomość WM_ERASEBKGND, a następnie wysyła wiadomość wM_PAINT-prawdopodobnie mapuje to do Twojej metody WX:: OnDraw. Następnie przechodzi przez kontrolę nad każdym dzieckiem i sprawia, że malują się same.

Jeśli to jest twój scenariusz... korzystanie z Vistas new Aero look rozwiązałoby twój problem, ponieważ Menedżer okien aero desktop automatycznie komponuje okna. Z starszy menedżer okien to pita.

 0
Author: Chris Becke,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-13 15:58:23