Paint Geschwindigkeit

Tim Bureck

Erfahrenes Mitglied
GDI+ Geschwindigkeit

Hallo zusammen,

ich habe das Problem, dass bei einem eigenen Usercontrol das Zeichnen ab einer gewissen Größe immer länger dauert (ja, irgendwie logisch ;)). Zugegeben, der Zeichenalgorithmus ist sehr einfach gehalten. Wie könnte ich das optimieren?

C#:
        protected override void OnPaint(PaintEventArgs pe) {
            base.OnPaint(pe);
            Graphics g = pe.Graphics;

            if (this.CarView != null) {
                Color c = NFZColor32.TRANSPARENT.Color;
                Color transparentMarker = Color.FromArgb(0, 140, 0);

                for (int y = 0; y < this.RealHeight; y++) {
                    for (int x = 0; x < this.RealWidth; x++) {
                        c = ((NFZColor32)this.CarView.Pixels[x / this.ZoomGraphic, y / this.ZoomGraphic]).Color;

                        g.FillRectangle(new SolidBrush(c), x * this.OpticalZoom, y * this.OpticalZoom, this.OpticalZoom, this.OpticalZoom);

                        if (c == NFZColor32.TRANSPARENT.Color && this.OpticalZoom >= 3) {
                            g.DrawRectangle(
                                new Pen(transparentMarker),
                                x * this.OpticalZoom + (this.OpticalZoom / 2 - 1),
                                y * this.OpticalZoom + (this.OpticalZoom / 2 - 1),
                                this.OpticalZoom % 2 == 0 ? 2 : 1,
                                this.OpticalZoom % 2 == 0 ? 2 : 1
                            );
                        }
                    }
                }
            }
        }

Bereits bei OpticalZoom 1 und ZoomGraphics 4 (RealHeight = 40; RealWidth = 128) kann man dem Control schön beim Zeichnen zusehen. Was beim OpticalZoom 16 passiert könnt ihr euch dann ausmalen. :rolleyes:

Best regards
 
Zuletzt bearbeitet:

Tim Bureck

Erfahrenes Mitglied
Gibt es vielleicht eine alternative Grafikschnittstelle, die ich hier einbauen könnte ohne die ganze GUI neu zu machen? Habe gelesen, dass die Geschwindigkeit von der CPU-Lastigkeit von GDI+ abhängt... aber keinen wirklichen Plan, wie ich das alternativ gestalten könnte.
 

napstermania

Mitglied
Hallo,

mit der GDI+ wird man nie große Zeichnungen und kurzer Zeit erledigen können. Wie du schon gesagt hast berechnet die GDI+ so ziemlich alles über den Prozessor und nicht über die Grafikkarte. Und da der Prozessor idR genug andere Sachen zu tun hat wird das Zeichnen sehr langsam. Ich empfehle dir XNA.
Das ist ein 3D Grafik Framework, welches auch direkt die Grafikkarte (btw Direct X) zum Zeichnen benutzt. Damit bekommt man sehr schnelle Zeichenroutinen hin.

Grüsse
 

Tim Bureck

Erfahrenes Mitglied
Das klingt erstmal interessant... Allerdings noch ein Paar Fragen:

1. Muss der Endbenutzer dieses Framework ebenfalls installieren, wenn ich das in meiner Software verwende?
2. Könnte ich damit überhaupt mein Windows Forms User Control zeichnen (z.B. jetzt in meiner OnPaint-Methode)?
 

Spyke

Premium-User
1. Pen und SolidBrush sind beide IDisposable und sollten dem entsprechend auch wieder freigegeben werden.
2. Dann würde ich wirklich nur denn sichtbaren Bereich zeichnen lassen (gibt es eine Eigenschaft im Graphics Object, VisibleRectangle glaube)
3. Du könntest noch versuchen das ganze mit DrawPath zu zeichnen.
 

Tim Bureck

Erfahrenes Mitglied
So, ich habe meinen Algorithmus noch ein mal ein bisschen überdacht und mir die Tipps hier zu Herzen genommen. Der Teilbereich wurde anscheinend bereits gezeichnet (sieht jedenfalls so aus, wenn man scrollt), dahingehend hab ich also nichts geändert. Allerdings hat etwas ganz banales schon einiges geholfen, hier der Algorithmus:

C#:
        protected override void OnPaint(PaintEventArgs pe) {
            base.OnPaint(pe);
            Graphics g = pe.Graphics;

            g.CompositingMode = CompositingMode.SourceCopy;
            g.CompositingQuality = CompositingQuality.HighSpeed;
            g.InterpolationMode = InterpolationMode.NearestNeighbor;
            g.SmoothingMode = SmoothingMode.None;
            g.PixelOffsetMode = PixelOffsetMode.None;

            if (this.CarView != null) {
                Color c = NFZColor32.TRANSPARENT.Color;
                Color transparentMarker = Color.FromArgb(0, 140, 0);
                SolidBrush brush = new SolidBrush(c);
                Pen pen = new Pen(transparentMarker);
                INFZColor[,] pixels = this.CarView.Pixels;

                g.Clear(c);

                for (int y = 0; y < this.RealHeight; y++) {
                    for (int x = 0; x < this.RealWidth; x++) {
                        c = ((NFZColor32) pixels[x / this.ZoomGraphic, y / this.ZoomGraphic]).Color;
                        brush.Color = c;

                        if (c == NFZColor32.TRANSPARENT.Color && this.OpticalZoom >= 3) {
                            g.DrawRectangle(
                                pen,
                                x * this.OpticalZoom + 1,
                                y * this.OpticalZoom + 1,
                                this.OpticalZoom - 2,
                                this.OpticalZoom - 2
                            );
                        } else {
                            g.FillRectangle(brush, x * this.OpticalZoom, y * this.OpticalZoom, this.OpticalZoom, this.OpticalZoom);
                        }
                    }
                }

                brush.Dispose();
                pen.Dispose();
            }
        }

Ich habe aus den Schleifen ein paar Dinge rausgezogen, was die Sache schon sehr viel schneller macht. Desweiteren ist die Darstellung von dem hellgrünen Quadrat vereinfacht worden. Vor allem das rausziehen des Array (CarView.Pixels) hat einen enormen Geschwindigkeitsschub gebracht. Mit der jetzigen Lösung kann ich erstmal leben, bis ich mir fortgeschritteneres Wissen (DirectX etc.) angeeignet hab.
 

colblake

Erfahrenes Mitglied
Einen Tip noch:

So wie du das programmiert hast, wird das Bild jedemal neu berechnet, wenn die OnPaint-Methode aufgerufen wird.
Wenn das nicht willst, zeichne doch auf ein Image (in einer separaten Methode) und stelle dann nur das Image dann in der OnPaint-Methode dar.

So kannst du die Erstellung des Bildes und den Zeitpunkt der Darstellung trennen.
 
Zuletzt bearbeitet:

Tim Bureck

Erfahrenes Mitglied
Mittlerweile gibts keinerlei Flackern mehr beim Zeichnen. Die Lösung des Problems war, das Propertiy DoubleBuffered auf true zu setzen. Läuft wie am Schnürchen!

C#:
public NFZDrawPanel {
  ...
  this.DoubleBuffered = true;
  ...
}