C#: flikkeringen in tekenprogramma voorkomen/verminderen

Pagina: 1
Acties:

Onderwerpen


Acties:
  • 0 Henk 'm!

  • huub8
  • Registratie: Maart 2009
  • Laatst online: 28-06-2021
Ik heb voor school een soort vector tekenprogramma geschreven in C#, echter begint het beeld behoorlijk te "flikkeren" als je eenmaal veel figuren hebt getekend, omdat hij dan elke keer bij de paint event al deze figuren opnieuw moet tekenen, en naarmate je meer figuren hebt getekend duurt dit dus langer in is het tekenen van de figuren als het ware zichtbaar door het flikkeren.

Dit flikkeren is vooral vervelend doordat het continu tijdens het tekenen gebeurd omdat wanneer je in mijn programma bijvoorbeeld een lijn tekent en op het startpunt hebt geklikt, dan tekent het programma steeds een soort van preview van de lijn naar het punt waar de cursor zich op dat moment bevindt (het figuur wordt pas echt aangemaakt als je op een tweede punt klikt). Maar elke keer als de muis beweegt moet dus de paint event van het formulier waarop wordt getekend worden aangeroepen en alle objecten opnieuw worden getekend, wat dus zorgt voor veel flikkering.

Het liefst zou ik nu dit zo veel mogelijk verminderen, zelf dacht ik aan het maken van een soort afbeelding van het formulier bij aanvang van het tekenen, zodat bij het alleen bewegen van de muis niet steeds alle figuren opnieuw moeten worden getekend maar alleen eerst deze afbeelding en daaroverheen het figuur wat op dat moment wordt getekend, als men dan klaar is met tekenen van een figuur moeten wel weer gewoon alle objecten eenmaal worden getekend.

Ik kom er echter niet uit hoe ik dit kan realiseren, het enige wat ik vond was een ToBitmap methode van het Graphics object, maar ook op het internet kon ik niet echt voorbeelden vinden die aansloten bij mijn probleem.

Kan iemand mij hiermee helpen, of kent iemand een betere oplossing?

Acties:
  • 0 Henk 'm!

  • Phyxion
  • Registratie: April 2004
  • Niet online

Phyxion

_/-\o_

Zoek eens op Double Buffering, daar is echt ruim voldoende over te vinden.

'You like a gay cowboy and you look like a gay terrorist.' - James May


Acties:
  • 0 Henk 'm!

  • huub8
  • Registratie: Maart 2009
  • Laatst online: 28-06-2021
Ben nu aan het zoeken (ben nog zeer onbekend met programmeren, dus kende die term nog niet)

Acties:
  • 0 Henk 'm!

  • ThaStealth
  • Registratie: Oktober 2004
  • Laatst online: 11-09 10:19
Komt eropneer dat je niet op het scherm tekent (wat traag is) maar in een buffer tekent, en de buffer in 1x naar het scherm jaagt.

Mess with the best, die like the rest


Acties:
  • 0 Henk 'm!

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 17-09 14:05

.oisyn

Moderator Devschuur®

Demotivational Speaker

ThaStealth schreef op dinsdag 08 juni 2010 @ 18:22:
Komt eropneer dat je niet op het scherm tekent (wat traag is) maar in een buffer tekent
Nou dat valt tegenwoordig ook wel mee. Videomem is doorgaans als write combined geheugen in je address space gemapped en dan heb je zo goed als geen stalls als je daarnaar schrijft (die heb je pas als je terugleest).

Het performancevoordeel zit 'm vooral in het feit dat het paint event wordt aangeroepen zodra er een stuk van je window zichtbaar is geworden. Het is natuurlijk onhandig om bij afhandeling van een dergelijk event elke keer alles opnieuw te gaan tekenen. Beter teken je eerst naar een offscreen buffer (wat best in videomem mag staan, dus wat dat betreft net zo snel!) als de contents daadwerkelijk wijzigen, en kopiëer je die contents naar het scherm bij vrijkomst van een stukje window, zodat je niet continu alles tekent terwijl een andere window voor je eigen window langs beweegt.

Tevens voorkomt dit flikkering (omdat tussentijdse resultaten zoals een buffer clear niet meteen zichtbaar zijn). Maar die flikkering staat dus los van de performance.

[ Voor 6% gewijzigd door .oisyn op 08-06-2010 18:41 ]

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


Acties:
  • 0 Henk 'm!

  • huub8
  • Registratie: Maart 2009
  • Laatst online: 28-06-2021
Ik snap nu wat het inhoud, maar in mijn geval denk ik dat double buffering nog niet voldoende is, want hierbij flikkert het dan niet echt meer, maar het stottert omdat het nog steeds lang duurt om alle figuren te tekenen elke keer als de "preview" veranderd. Maar het is dus voor mij eigenlijk niet nodig alle figuren te tekenen, ik weet immers dat alleen de gene die op dat moment wordt getekend door de gebruiker steeds veranderd, de al getekende figuren blijven gelijk. Dus zou ik graag een afbeeldingen vooraf maken van alle al getekende figuren en dan alleen en figuur dat door de gebruiker word getekend daaroverheen tekenen, nu kom ik er echter niet uit hoe ik als het ware een afbeelding van een graphics object kan maken, waar ik het graphics object dan later steeds weer aan gelijk kan stellen...

Acties:
  • 0 Henk 'm!

  • NickThissen
  • Registratie: November 2007
  • Laatst online: 09-09 10:50
Om je 'papier' opnieuw te tekenen neem ik aan dat je ofwel een Invalidate ofwel een Refresh call maakt op je Panel of PictureBox of whatever je gebruikt om je figuren op te tekenen.

De Invalidate methode kan een Rectangle als argument accepteren. Hij zal dan alleen die Rectangle opnieuw tekenen, wat mogelijk veel sneller is. Als jij een lijn aan het tekenen bent zou je in principe alleen de rechthoek van linkerboven hoek tot rechteronder hoek opnieuw hoeven te tekenen (de rest verandert toch niet), dus kun je die rechthoek als argument in the Invalidate methode gebruiken.

Dit helpt trouwens vooral voor de snelheid van het tekenen. Voor het 'flickeren' is het toch echt double buffering waar je naar moet kijken. Meestal is het genoeg om gewoon de DoubleBuffered property van je Panel/PictureBox/whatever op True te zetten. Als die property niet public is (weet ik zo even niet) dan zul je dus een class moeten maken die van Panel/PictureBox/whatever overerft, en die property (bijvoorbeeld) in de constructor zet:
C#:
1
2
3
4
5
6
7
public class Canvas : System.Windows.Forms.Panel
{
   public Canvas()
   {
      this.DoubleBuffered = true;
   }
}


Als alternatief kun je ook vier 'ControlStyles' zetten mbv de SetStyle methode. Ik weet zo uit m'n hoofd even niet of de naam van alle 4 de argumenten correct is, maar mbv intellisense zou je de juiste moeten kunnen vinden.
C#:
1
2
3
4
5
6
7
8
9
10
public class Canvas : System.Windows.Forms.Panel
{
   public Canvas()
   {
      this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
      this.SetStyle(ControlStyles.UserPaint, true);
      this.SetStyle(ControlStyles.ResizeRedraw, true);
      this.SetStyle(ControlStyles.DoubleBuffer, true);
   }
}

Merk op de de ControlStyles.DoubleBuffer waarde niet in intellisense voorkomt. Hij wordt verborgen met een Browsable(false) attribuut, maar je kunt het wel gewoon gebruiken.

Ik geloof dat de DoubleBuffered property een 'shortcut' is voor deze vier styles, dus in principe zou dat wel genoeg moeten zijn.

Mijn iRacing profiel


Acties:
  • 0 Henk 'm!

  • roy-t
  • Registratie: Oktober 2004
  • Laatst online: 08-09 11:33
huub8 schreef op dinsdag 08 juni 2010 @ 19:21:
Ik snap nu wat het inhoud, maar in mijn geval denk ik dat double buffering nog niet voldoende is, want hierbij flikkert het dan niet echt meer, maar het stottert omdat het nog steeds lang duurt om alle figuren te tekenen elke keer als de "preview" veranderd. Maar het is dus voor mij eigenlijk niet nodig alle figuren te tekenen, ik weet immers dat alleen de gene die op dat moment wordt getekend door de gebruiker steeds veranderd, de al getekende figuren blijven gelijk. Dus zou ik graag een afbeeldingen vooraf maken van alle al getekende figuren en dan alleen en figuur dat door de gebruiker word getekend daaroverheen tekenen, nu kom ik er echter niet uit hoe ik als het ware een afbeelding van een graphics object kan maken, waar ik het graphics object dan later steeds weer aan gelijk kan stellen...
Bij stotteren is je 'frame rate' gewoon te laag. Blijkbaar is het te duur voor de processor om je graphics te tekenen binnen 1/60e van een seconde. Je moet dan meer gaan kijken naar optimalisaties in je schrijf code, of op een heel andere manier gaan tekenen. Ttekenen met winforms en de draw functies is vrij traag, voor performante drawing kun je kijken naar bijvoorbeeld XNA, dat kun je gebruiken icm winforms, is wel wat omschrijf werk helaas.

~ Mijn prog blog!


Acties:
  • 0 Henk 'm!

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 17-09 14:05

.oisyn

Moderator Devschuur®

Demotivational Speaker

Winforms layert toch gewoon op GDI? Da's op zich snel zat, als je het maar gewoon slim aanpakt. Dirty rectangles zijn idd de way to go. Zelf heb ik ooit eens een graph view gemaakt voor een muziekprogramma waarin je verschillende elementen aan elkaar kunt koppelen.

Afbeeldingslocatie: http://oisyn.nl/pics/machinery2.png

Al die componenten zijn gewoon te draggen en de connecties veranderen live mee. Dit heb ik (weliswaar in win32) geïmplementeerd door gebruik te maken van Windows' regions. Naast InvalidateRect() heb je namelijk ook InvalidateRgn(). Regions zijn intern in feite gewoon een lijst met rectangles. Sowieso had ik natuurlijk een offscreen bitmap, en ik hield zelf een dirty region bij. Bij een paint event ging ik eerst het dirty gedeelte van de offscreen bitmap opnieuw tekenen door over mijn componenten heen te lopen en te kijken of zij intersecten met een region (met de RectInRegion() functie), en zo ja dan teken ik ze. Na dit alles doe ik gewoon een simpele blit van mijn offscreen bitmap naar het scherm. Windows maakt daarbij weer gebruik van zijn eigen dirty region die gezet is door InvalidateRgn(), en zal daardoor dus alleen die dingen blitten die veranderd zijn.

Als het tekenen alsnog te langzaam gaat zou je idd kunnen denken aan een aparte offscreen bitmap waar alle componenten op staan die je niet aan het draggen bent, die blit je dan weer naar je algemente offscreen bitmap, vervolgens teken je de te draggen componenten er op en dat blit je vervolgens weer naar het scherm. Maar over het algemeen heb je dit dus niet eens nodig. Hier een wat complexere view waar bij de geselecteerde componenten (in rood/roze) gewoon vloeiend heen en weer te slepen zijn, inclusief updates van alle verbindingen.

[ Voor 8% gewijzigd door .oisyn op 09-06-2010 11:18 ]

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


Acties:
  • 0 Henk 'm!

  • farlane
  • Registratie: Maart 2000
  • Laatst online: 16-09 22:43
.oisyn schreef op woensdag 09 juni 2010 @ 11:09:
Winforms layert toch gewoon op GDI?
Volgens mij zijn de lagen vrij dik en talrijk.

Somniferous whisperings of scarlet fields. Sleep calling me and in my dreams i wander. My reality is abandoned (I traverse afar). Not a care if I never everwake.


Acties:
  • 0 Henk 'm!

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 17-09 14:05

.oisyn

Moderator Devschuur®

Demotivational Speaker

Dat zou kunnen idd. Wellicht is het zelfs GDI+, wat sowieso niet al te best performt :)

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.

Pagina: 1