[C#] Onderscheiden Windows vs Applicatie getriggerde OnPaint

Pagina: 1
Acties:

Acties:
  • 0 Henk 'm!

  • Bozozo
  • Registratie: Januari 2005
  • Laatst online: 20-02 16:10

Bozozo

Your ad here?

Topicstarter
Ik werk momenteel aan een programmaatje dat een audiospectrum weergeeft. Daarbij wordt meerdere malen per seconde een control fullscreen gerepaint. Dat heb ik zo efficient mogelijk geprobeerd te maken met de volgende code:

C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
        protected override void OnPaint(PaintEventArgs e)
        {
            // If Windows requested this repaint, do a full redraw
            if (!(newData || newAxes || newSize))
                newAxes = true;

            if (newSize)
            {
                // Graph size changed.
                // Create a new offscreen bitmap to draw on
                drawCanvas();
            }
            if (newAxes)
            {
                // Axes dimensions changed.
                // Draw new axes onto offScreen bitmap
                currentAxes = futureAxes;
                drawAxes();
                e.Graphics.DrawImage(offScreenBitmap, 0, 0);

                // Draw current data in new axes
                newData = true;
            }
            if (newData)
            {
                // Channel data changed

                // Draw white over old lines
                foreach (Point[] line in lines)
                {
                    e.Graphics.DrawLines(this.eraseLinePen, line);
                }

                // Compute new lines
                drawData();

                // Draw new lines
                for (int i = 0; i < lines.Count; i++)
                {
                    this.drawLinePen.Color = channelColors[i];
                    e.Graphics.DrawLines(this.drawLinePen, lines[i]);
                }
            }
            newSize = newAxes = newData = false;
        }

        protected override void OnPaintBackground(PaintEventArgs e)
        {
            // Ignore
        }


Als er bijvoorbeeld nieuwe data getekend moet worden gebeurt er dit:
C#:
1
2
3
4
5
6
7
8
9
        public void requestDataDraw()
        {
            // New data is ready to be drawn
            newData = true;

            // Redraw data area (defined in Rectangle graphRect)
            else
                this.Invalidate(graphRect);
        }


Dat loopt als een zonnetje (~1% CPU belasting bij fullscreen 8x per seconde tekenen) en werkt nagenoeg perfect. Als windows een repaint triggert (bijvoorbeeld omdat ik een ander venster, dat de grafiek overlapte, minimaliseer) wordt de boel netjes opnieuw getekend.

Het probleem van deze constructie wordt duidelijk als ik bijvoorbeeld de Task Manager boven mijn grafiek zet en dan op kruisje druk (in de Task Manager) terwijl de spectrum analyzer draait:
Afbeeldingslocatie: http://i2.photobucket.com/albums/y6/Bozozo/paint_issue.png

Wat gebeurt er? Windows triggert een Paint event omdat de Task Manager verdwijnt. Normaal gesproken zou de OnPaint handler vrijwel onmiddelijk zijn uitgevoerd, waarbij een full redraw wordt gedaan omdat alle newXxxx vlaggen false zijn. Het systeem is echter even druk met het afsluiten van de Task Manager, waardoor het uitvoeren van deze OnPaint even op zich laat wachten. Tijdens deze periode komt er nieuwe data binnen, waardoor de newData flag op true wordt gezet. Hierdoor ziet mijn OnPaint handler niet meer dat hij eerst door Windows was getriggered, en in plaats van een full redraw wordt alleen de data opnieuw getekend.

Na deze veel te lange introductie de vraag: hoe kan ik nog het onderscheid maken tussen een door Windows getriggerde OnPaint en een door mijzelf middels this.Invalidate() getriggerde OnPaint()?

De logische oplossing zou zijn om de OnInvalidate handler een vlaggetje te laten zetten (als in userTriggered = true) maar dat maakt geen verschil want de OnPaint wordt per definitie pas na de OnInvalidate gedaan, dus in het probleemgeval zou userTriggered gewoon true zijn.

TabCinema : NiftySplit


Acties:
  • 0 Henk 'm!

  • Woy
  • Registratie: April 2000
  • Niet online

Woy

Moderator Devschuur®
Is het niet handiger om in je OnPaint gewoon helemaal geen draw logica te stoppen, maar alleen je offscreen image naar het scherm kopieren.

Dan kun je verder gewoon losse Update* methodes hebben die je offscreen image updaten. Die methodes worden niet door de window manager getriggerd. Als er dan een resize o.i.d. plaatsvind dan roep je eerst de update methodes aan en daarna een invalidate.

Je hoeft dan in je OnPaint geen verschil te maken tussen een Applicatie of Window Manager triggered Paint

“Build a man a fire, and he'll be warm for a day. Set a man on fire, and he'll be warm for the rest of his life.”


Acties:
  • 0 Henk 'm!

  • CodeIT
  • Registratie: Juni 2002
  • Laatst online: 15-09 21:49

CodeIT

Code IT

Volgens mij moet dat kunnen door WndProc te overriden en dan te filteren op WM_PAINT messages.
Hier wordt het, zij het in een andere context, ook gebruikt: http://www.codeproject.com/KB/list/listviewff.aspx

Acties:
  • 0 Henk 'm!

  • Bozozo
  • Registratie: Januari 2005
  • Laatst online: 20-02 16:10

Bozozo

Your ad here?

Topicstarter
Woy schreef op dinsdag 15 september 2009 @ 11:56:
Is het niet handiger om in je OnPaint gewoon helemaal geen draw logica te stoppen, maar alleen je offscreen image naar het scherm kopieren.

Dan kun je verder gewoon losse Update* methodes hebben die je offscreen image updaten. Die methodes worden niet door de window manager getriggerd. Als er dan een resize o.i.d. plaatsvind dan roep je eerst de update methodes aan en daarna een invalidate.

Je hoeft dan in je OnPaint geen verschil te maken tussen een Applicatie of Window Manager triggered Paint
Dat doe ik voor de achtergrond (wit vierkant, assen, labels), en dat is zelfs sneller dan onscreen zoveel tekenen. Als ik de data zo bij elke refresh redraw kost dat echter 30% CPU, en direct naar de control tekenen slechts 1%. Dan is mijn keuze snel gemaakt tenzij ik dit probleem niet krijg opgelost.

@CodeIT: Tnx, daar ga ik even naar kijken.
edit: werkt niet... message wordt tegelijk (net voor) de OnPaint trigger ontvangen. Dus het maakt geen verschil of ik naar dat bericht luister of naar OnPaint.
edit2: ik had nog even hoop dat windows een parameter zou zetten in die message waardoor ik verschil zou zien tussen Paint messages veroorzaakt door Windows of de applicatie. Niet dus :(

[ Voor 14% gewijzigd door Bozozo op 15-09-2009 12:43 ]

TabCinema : NiftySplit


Acties:
  • 0 Henk 'm!

  • Niemand_Anders
  • Registratie: Juli 2006
  • Laatst online: 09-07-2024

Niemand_Anders

Dat was ik niet..

Woy bedoeld eerder dat de plaats waar newData wordt gezet, dat deze zelf drawNewData aanroept en vergelijkbare aanroepen voor newAxis en newSize. Je verplaatst dan in feite de locaties waar wordt getekend. De OnPaint doet dan eigenlijk niets meer..

Maar ik zie dat je ook een dropdown 'update rate' hebt. Kun je hiermee niet een timer instellen welke een 'alternatieve' OnPaint aanroept? Als de timer afgaat stop je de timer, je redrawed de bitmap en daarna start je de timer weer. Op die manier kun je niet meerdere events door elkaar krijgen..

If it isn't broken, fix it until it is..


Acties:
  • 0 Henk 'm!

  • Bozozo
  • Registratie: Januari 2005
  • Laatst online: 20-02 16:10

Bozozo

Your ad here?

Topicstarter
Niemand_Anders schreef op dinsdag 15 september 2009 @ 12:19:
Woy bedoeld eerder dat de plaats waar newData wordt gezet, dat deze zelf drawNewData aanroept en vergelijkbare aanroepen voor newAxis en newSize. Je verplaatst dan in feite de locaties waar wordt getekend. De OnPaint doet dan eigenlijk niets meer..
Dat gaat niet zomaar. Het idee van deze methode van tekenen is dat ik zeker weet dat het canvas en de assen eerst worden getekend, en dat dan de grafiekpunten worden berekend. Anders loop je het risico dat je de datapunten berekent en dat daarna de assen worden aangepast, waardoor de data verkeerd in de assen wordt getekend.
Maar ik zie dat je ook een dropdown 'update rate' hebt. Kun je hiermee niet een timer instellen welke een 'alternatieve' OnPaint aanroept? Als de timer afgaat stop je de timer, je redrawed de bitmap en daarna start je de timer weer. Op die manier kun je niet meerdere events door elkaar krijgen..
Die dropdown bepaalt de lengte van de audio buffer. De onPaint wordt aangeroepen als deze buffer vol en verwerkt is. Een timer introduceert waarschijnlijk nieuwe problemen, en lost het huidige probleem m.i. niet op omdat je evenveel Invalidate() calls per seconde krijgt en die nog steeds kunnen samenvallen met een door windows gegenereerd Paint event.


Ik denk dat ik moet gaan kijken naar Refresh() ipv Invalidate, aangezien deze een onmiddelijke repaint forceert. Helaas moet ik dan met threads gaan werken en daar heb ik eigenlijk weinig zin in :(

Edit: net een ingeving gehad. Eigenlijk heel simpel... een redraw veroorzaakt door een window dat afsluit boven mijn applicatie zal ook een Paint event triggeren op het parent Form. De Invalidate() calls niet. Zo kan ik dus onderscheid maken :)
edit edit: dat werkte in praktijk dus niet. Het duurt heel lang (~seconden) voor het onPaint event wordt gevuurd nadat ik de Task Manager afsluit. WTF :(

[ Voor 11% gewijzigd door Bozozo op 15-09-2009 19:08 ]

TabCinema : NiftySplit


Acties:
  • 0 Henk 'm!

  • MSalters
  • Registratie: Juni 2001
  • Laatst online: 13-09 00:05
Bozozo schreef op dinsdag 15 september 2009 @ 11:44:
Ik werk momenteel aan een programmaatje dat een audiospectrum weergeeft. Daarbij wordt meerdere malen per seconde een control fullscreen gerepaint. ... Windows triggert een Paint event omdat de Task Manager verdwijnt. Normaal gesproken zou de OnPaint handler vrijwel onmiddelijk zijn uitgevoerd, waarbij een full redraw wordt gedaan omdat alle newXxxx vlaggen false zijn. Het systeem is echter even druk met het afsluiten van de Task Manager, waardoor het uitvoeren van deze OnPaint even op zich laat wachten. Tijdens deze periode komt er nieuwe data binnen, waardoor de newData flag op true wordt gezet. Hierdoor ziet mijn OnPaint handler niet meer dat hij eerst door Windows was getriggered, en in plaats van een full redraw wordt alleen de data opnieuw getekend.

Na deze veel te lange introductie de vraag: hoe kan ik nog het onderscheid maken tussen een door Windows getriggerde OnPaint en een door mijzelf middels this.Invalidate() getriggerde OnPaint()?
Dat is de verkeerde aanpak. Je moet in OnPaint(PaintEventArgs e) de invalid rechthoek uit e painten. Het maakt niet uit waar die e vandaan komt. Als je een full redraw will triggeren dan doe je dat niet via vlaggetjes, maar je invalidate simpelweg het hele window. Gevolg: een OnPaint event voor dat hele window.

Man hopes. Genius creates. Ralph Waldo Emerson
Never worry about theory as long as the machinery does what it's supposed to do. R. A. Heinlein


  • Bozozo
  • Registratie: Januari 2005
  • Laatst online: 20-02 16:10

Bozozo

Your ad here?

Topicstarter
Het maakt voor mij wel degelijk uit waar die e vandaan komt. Als ik een Invalidate trigger omdat er nieuwe data is wil ik niet de hele offscreen bitmap naar de control tekenen, maar alleen nieuwe lijntjes trekken. Dat is namelijk veel sneller dan een hele bitmap painten.

Een door windows getriggerde onPaint zal altijd de volledige offscreen bitmap naar de control tekenen (wel een goed idee om alleen de invalidated rectangle te doen!) , waarna de data er overheen wordt getekend.

Ik snap wel dat het probleem wordt opgelost door bij elke onPaint de offscreen bitmap in de invalidated rectangle te tekenen, maar dat wil ik niet omdat het te langzaam is.

TabCinema : NiftySplit


  • Janoz
  • Registratie: Oktober 2000
  • Laatst online: 16-09 09:15

Janoz

Moderator Devschuur®

!litemod

Goed, leg je werk neer. Doe een paar stappen terug. Maak je hoofd leeg en lees vervolgens nogmaals de verschillende reacties. Bedenk daarbij dat het antwoord op de vraag:

Hoe weet ik of een repaint van mijn applicatie komt of van windows?

eigenlijk heel simpel is, namelijk:

Je weet welke van je applicatie komt aangezien je die zelf triggered, dus de rest zal wel van windows komen.


Waarom moet de data grafiek perse rechtstreeks op het scherm getekend worden?

Waarom zou je maar 1 offscreen buffer mogen hebben?
maar alleen nieuwe lijntjes trekken. Dat is namelijk veel sneller dan een hele bitmap painten.
Denk je dat of weet je dat?

[ Voor 11% gewijzigd door Janoz op 16-09-2009 10:57 ]

Ken Thompson's famous line from V6 UNIX is equaly applicable to this post:
'You are not expected to understand this'


  • Bozozo
  • Registratie: Januari 2005
  • Laatst online: 20-02 16:10

Bozozo

Your ad here?

Topicstarter
Dat weet ik. Ik heb beide wegen gevolgd en direct tekenen is tientallen keren sneller. Dat is ook logisch als je kijkt naar het aantal pixels dat moet worden getekend (= breedte van bitmap in pixels als je alleen lijntjes trekt, of hoogte * breedte van bitmap bij volledige paint van offscreen bitmap naar control)

edit:

En ik weet inderdaad of zo'n repaint door mijn applicatie wordt veroorzaakt:
- Vlaggetje? => Voer adequate beperkte repaint uit.
- Geen vlaggetjes gezet? Blijkbaar vraagt windows zelf een repaint aan. => Doe volledige repaint.

Het probleem treedt logischerwijs op als windows een repaint aanvraagt, en meteen daarna een vlaggetje wordt gezet. Dan volgt er onterecht geen volledige repaint.

Ik moet inderdaad eens van onderaf opnieuw beginnen, maar ik zie niet echt een goede oplossing die de huidige snelheid evenaart en het huidige probleem omzeilt.

[ Voor 90% gewijzigd door Bozozo op 16-09-2009 11:11 ]

TabCinema : NiftySplit


  • R4gnax
  • Registratie: Maart 2009
  • Laatst online: 06-09 17:51
Bozozo schreef op woensdag 16 september 2009 @ 11:01:
Dat weet ik. Ik heb beide wegen gevolgd en direct tekenen is tientallen keren sneller. Dat is ook logisch als je kijkt naar het aantal pixels dat moet worden getekend (= breedte van bitmap in pixels als je alleen lijntjes trekt, of hoogte * breedte van bitmap bij volledige paint van offscreen bitmap naar control)
Goh. Waar ik vandaan kom is een canvas locken, direct memory blitten van één rechthoek aan bitmap data en het canvas weer unlocken toch nog altijd sneller dan het canvas locken, één voor één pixels gaan tekenen en het canvas weer unlocken. Laat staan als je niet eens locked maar de 'safe code' versie SetPixel() gebruikt.

Lijkt er op dat je iets heeeeeeel erg verkeerd aan het doen bent.

Los van het tekenen zou ik sowieso eens heel kritisch gaan kijken naar de manier waarop je heel je update mechanisme in elkaar hebt gestoken. Misschien zou het nl. een goed idee zijn om dat eens double-buffered te maken: één buffer om vanuit een secundaire thread bij te werken, één buffer die klaar staat om naar het scherm geblit te worden zodra er een OnPaint plaats vindt.

Probleem opgelost.

  • Bozozo
  • Registratie: Januari 2005
  • Laatst online: 20-02 16:10

Bozozo

Your ad here?

Topicstarter
Lees even mijn code door. Ik gebruik voor het tekenen van de data helemaal geen bitmap. Als er data moet worden getekend invalideer ik de grafiek region op de control, en in de veroorzaakte OnPaint call ik de (managed) functie drawLines direct op de Graphics van het Paint event. Ik hoef dus geen offscreen bitmap naar het scherm te tekenen als er nieuwe data binnenkomt. Canvas locken oid is helemaal niet aan de orde.

TabCinema : NiftySplit


  • Janoz
  • Registratie: Oktober 2000
  • Laatst online: 16-09 09:15

Janoz

Moderator Devschuur®

!litemod

R4gnax heeft het niet over je code, maar over je probleem. Ook ik heb het gevoel dat de code die jij gebruikt hebt voor het blitten erg inefficient is geweest waardoor je testje je misschien de verkeerde indruk gegeven heeft. Ik heb geen specifieke kennis van .Net, maar in mijn beleving was het blitten altijd razend snel.

Het punt is dat zo ongeveer iedereen in deze thread de volgende structuur voorstelt:

1 Een onPaint die enkel de (relevante) delen uit een backbuffer naar de control blit

2 Alles wat je nu in de onPaint hebt staan in een apparte methode die alles naar een backbuffer rendered en zodra deze klaar is de standaard onPaint aanroept om de boel op het scherm te krijgen


Bij 2 kun je vervolgens het hele paint proces opsplitsen en gebruik maken van meerdere buffers. Een voorbeeld kan een extra buffer voor je assenstelsel zijn die je alleen aanpast waneer de assen aangepast worden. Deze blit je vervolgens in de andere buffer waar je vervolgens de grafiek overheen tekent.

Door deze opsplitsing heb je helemaal geen last van veranderende vlaggetjes tijdens de onpaint. Eventueel kun je overwegen om een double backbuffer aan te houden zodat zelfs 'tearing' volledig voorkomen kan worden.

Ken Thompson's famous line from V6 UNIX is equaly applicable to this post:
'You are not expected to understand this'


  • Bozozo
  • Registratie: Januari 2005
  • Laatst online: 20-02 16:10

Bozozo

Your ad here?

Topicstarter
Ik begrijp het idee van blitten en een buffer bijhouden en alles. Als je veel ingewikkelde pixel manipulaties doet is dat ongetwijfeld de beste methode.

Mijn punt is dat, hoe slim je dat blitten ook doet, je uiteindelijk je offscreen buffer naar het scherm moet zien te krijgen. Dat doe je vziw middels e.Graphics.drawImage(offscreenBitmap, 0, 0) in je OnPaint event. Deze operatie forceert het repainten van hoogte*breedte pixels, en dat is een hele dure operatie.

Ik denk dus dat voor het tekenen van een simpele lijn het bijhouden van een buffer niet efficiënt is. Zit ik daarmee verkeerd?

TabCinema : NiftySplit


  • Janoz
  • Registratie: Oktober 2000
  • Laatst online: 16-09 09:15

Janoz

Moderator Devschuur®

!litemod

Bozozo schreef op woensdag 16 september 2009 @ 12:30:
Ik begrijp het idee van blitten en een buffer bijhouden en alles. Als je veel ingewikkelde pixel manipulaties doet is dat ongetwijfeld de beste methode.

Mijn punt is dat, hoe slim je dat blitten ook doet, je uiteindelijk je offscreen buffer naar het scherm moet zien te krijgen. Dat doe je vziw middels e.Graphics.drawImage(offscreenBitmap, 0, 0) in je OnPaint event. Deze operatie forceert het repainten van hoogte*breedte pixels, en dat is een hele dure operatie.

Ik denk dus dat voor het tekenen van een simpele lijn het bijhouden van een buffer niet efficiënt is. Zit ik daarmee verkeerd?
Offscreen buffer naar het scherm is het blitten. Het punt is dat het al volledig in het juiste vormaat is dus het is enkel een kwestie van een bak data van ergens in het geheugen te kopieren naar een plek in het video geheugen. En dat kan behoorlijk rap en is een stuk minder duur dan jij denkt. Het is zelfs zo dat dat een stuk sneller kan zijn dan het manipuleren van een paar pixels in die buffer. Tot slot kun je die draw image ook heel simpel ombouwen zodat hij alleen een relevant deel opnieuw doet waardoor een partial repaint heel simpel te implementeren is.

Ken Thompson's famous line from V6 UNIX is equaly applicable to this post:
'You are not expected to understand this'


  • Bozozo
  • Registratie: Januari 2005
  • Laatst online: 20-02 16:10

Bozozo

Your ad here?

Topicstarter
Ok ik zal het gaan uitzoeken. Waarschijnlijk hebben jullie wel gelijk (A)


Voorlopig word ik nog even knettergek van mijn huidige implementatie. Ik call nu Update() direct na elke Invalidate, zodat ik zeker weet dat er onmiddelijk een OnPaint wordt getriggerd. Ik dacht, mooie oplossing. Niet dus... het probleem is er nog steeds. Ik snap er echt niks van :'(

edit: Ach fok het ook. Heb mn hele architectuur overboord gegooid en teken nu alles naar een offscreen bitmap. Bij een OnPaint teken ik die bitmap naar het geinvalideerde gebied op de control. Dat is dus hoe ik het tientallen uren frustratie geleden ook deed... Nu ga ik dat blitten maar eens proberen, om weer een fatsoenlijke snelheid te bereiken.

[ Voor 32% gewijzigd door Bozozo op 16-09-2009 12:58 ]

TabCinema : NiftySplit


  • Bozozo
  • Registratie: Januari 2005
  • Laatst online: 20-02 16:10

Bozozo

Your ad here?

Topicstarter
Janoz schreef op woensdag 16 september 2009 @ 12:36:
[...]

Offscreen buffer naar het scherm is het blitten. Het punt is dat het al volledig in het juiste vormaat is dus het is enkel een kwestie van een bak data van ergens in het geheugen te kopieren naar een plek in het video geheugen. En dat kan behoorlijk rap en is een stuk minder duur dan jij denkt. Het is zelfs zo dat dat een stuk sneller kan zijn dan het manipuleren van een paar pixels in die buffer. Tot slot kun je die draw image ook heel simpel ombouwen zodat hij alleen een relevant deel opnieuw doet waardoor een partial repaint heel simpel te implementeren is.
Zou je me willen uitleggen hoe ik dit moet doen? Ik heb al uren gegoogled maar ik kan nergens een alternatief vinden voor e.Graphics.DrawImage(bmp,0,0) in de OnPaint handler om mijn buffer op het scherm te krijgen. Dit is met afstand de bottleneck van mijn huidige implementatie, en ik ben naarstig op zoek naar iets efficienters.

Ik overweeg ook om het met DirectX of XNA te gaan doen, maar ik zou voorlopig de huidige code eerst wat sneller willen krijgen.

TabCinema : NiftySplit


  • Janoz
  • Registratie: Oktober 2000
  • Laatst online: 16-09 09:15

Janoz

Moderator Devschuur®

!litemod

Ik ben niet bekend met .Net, maar het lijkt me dat dat wel behoorlijk snel zou moeten kunnen met die methode. Van java weet ik dat je goed op moet letten dat je image dezelfde eigenschappen heeft (kleurdiepte en volgorde van de bytes) als het scherm waar je op tekend. Op msdn zie ik ook geneuzel over dpi, zorg ook dat dat gelijk is. Verder zou ik het zo niet weten.

Ken Thompson's famous line from V6 UNIX is equaly applicable to this post:
'You are not expected to understand this'


Acties:
  • 0 Henk 'm!

  • Bozozo
  • Registratie: Januari 2005
  • Laatst online: 20-02 16:10

Bozozo

Your ad here?

Topicstarter
Uiteindelijke heb ik dit artikeltje gevonden. Volgens de auteur gebruikt .net achter de schermen BitBlt als je DrawImage aanroept. Overstappen van DrawImage naar bitblt zal me dus geen magische performance winst opleveren.

edit: dit is wellicht interessant...
edit: msdn doc over de managed doublebuffered manier van werken. Helaas niet merkbaar sneller dan handmatig dubbel bufferen in een bitmap. Edit: BufferedGraphics zonder doublebuffer of optimizeddoublebuffer is wel heel snel.

[ Voor 52% gewijzigd door Bozozo op 20-09-2009 19:54 ]

TabCinema : NiftySplit


Acties:
  • 0 Henk 'm!

  • Bozozo
  • Registratie: Januari 2005
  • Laatst online: 20-02 16:10

Bozozo

Your ad here?

Topicstarter
Laatste schopje voor het geval mensen dit topic volgen. Dit is (de relevante code van) mijn uiteindelijke oplossing; ongeveer tien keer zo snel (natte vingerwerk hoor) als het bijhouden van een eigen offscreen bitmap en die naar het scherm tekenen met drawImage. Met dank aan de prima documentatie op MSDN.

C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
class Graph : UserControl
{
    private List<Point[]> lines = new List<Point[]>();
    private BufferedGraphicsContext context;
    private BufferedGraphics grafx;
    private Graphics offScreenGraphics;
    private Object drawlock = new Object();

    public Graph()
    {
        SetStyle(ControlStyles.Opaque, true);
        SetStyle(ControlStyles.UserPaint, true);
        SetStyle(ControlStyles.AllPaintingInWmPaint, true);
        SetStyle(ControlStyles.OptimizedDoubleBuffer, false); //notice the 'false' !!
        
        context = BufferedGraphicsManager.Current;
    }

    protected override void OnResize(EventArgs e)
    {
        drawCanvas();
        drawData();
        this.Invalidate();
    }

    protected override void OnPaint(PaintEventArgs e)
    {
        lock(drawlock)
        {
            grafx.Render(e.Graphics);
        }
    }

    private void drawCanvas()
    {
        lock(drawlock)
        {
            context.MaximumBuffer = new Size(this.Width + 1, this.Height + 1);
            if (grafx != null)
            {
                grafx.Dispose();
                grafx = null;
            }
            grafx = context.Allocate(this.CreateGraphics(), new Rectangle(0, 0, this.Width, this.Height));
            grafx.Graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.Low;
            grafx.Graphics.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.None;
            grafx.Graphics.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighSpeed;
            offScreenGraphics = grafx.Graphics;
        }
    }

    public void requestDataDraw()
    {
        // Compute new lines
        drawData();

        this.Invalidate();
    }

    private void drawData()
    {
        lock(drawlock)
        {
            // Draw white over old lines
            foreach (Point[] line in lines)
            {
                offScreenGraphics.DrawLines(this.eraseLinePen, line);
            }
            lines.Clear();

            // compute new lines (refilling lines list)
            // ...

            // Draw new lines
            for (int i = lines.Count - 1; i >= 0; i--)
            {
                offScreenGraphics.DrawLines(this.drawLinePen, lines[i]);
                lines.Add(lines[i]);
            }
        }
    }
}

TabCinema : NiftySplit

Pagina: 1