14 Stimmen

SetPixel ist zu langsam. Gibt es einen schnelleren Weg, um zu Bitmap zeichnen?

Ich habe ein kleines Malprogramm, an dem ich gerade arbeite. Ich verwende SetPixel auf einer Bitmap, um die Linien zu zeichnen. Wenn die Pinselgröße groß wird, etwa 25 Pixel, gibt es einen spürbaren Leistungsabfall. Ich frage mich, ob es einen schnelleren Weg gibt, um in eine Bitmap zu zeichnen. Hier ist ein wenig der Hintergrund des Projekts:

  • Ich verwende Bitmaps, damit ich Ebenen verwenden kann, wie in Photoshop oder GIMP.
  • Die Linien werden manuell gezeichnet, da der Druck des Grafiktabletts verwendet wird, um die Größe der Linie über ihre Länge zu verändern.
  • Die Linien sollten schließlich an den Rändern geglättet/entschärft werden.

Ich füge meinen Zeichencode bei, nur für den Fall, dass dies langsam ist und nicht das Set-Pixel-Bit.

Dies ist das Fenster, in dem die Malerei stattfindet:

    private void canvas_MouseMove(object sender, MouseEventArgs e)
    {
        m_lastPosition = m_currentPosition;
        m_currentPosition = e.Location;

        if(m_penDown && m_pointInWindow)
            m_currentTool.MouseMove(m_lastPosition, m_currentPosition, m_layer);
        canvas.Invalidate();
    }

Implementierung von MouseMove:

    public override void MouseMove(Point lastPos, Point currentPos, Layer currentLayer)
    {
        DrawLine(lastPos, currentPos, currentLayer);
    }

Implementierung von DrawLine:

    // The primary drawing code for most tools. A line is drawn from the last position to the current position
    public override void DrawLine(Point lastPos, Point currentPos, Layer currentLayer)
    {
        // Creat a line vector
        Vector2D vector = new Vector2D(currentPos.X - lastPos.X, currentPos.Y - lastPos.Y);

        // Create the point to draw at
        PointF drawPoint = new Point(lastPos.X, lastPos.Y);

        // Get the amount to step each time
        PointF step = vector.GetNormalisedVector();

        // Find the length of the line
        double length = vector.GetMagnitude();

        // For each step along the line...
        for (int i = 0; i < length; i++)
        {
            // Draw a pixel
            PaintPoint(currentLayer, new Point((int)drawPoint.X, (int)drawPoint.Y));
            drawPoint.X += step.X;
            drawPoint.Y += step.Y;
        }
    }

Implementierung von PaintPoint:

    public override void PaintPoint(Layer layer, Point position)
    {
        // Rasterise the pencil tool

        // Assume it is square

        // Check the pixel to be set is witin the bounds of the layer

            // Set the tool size rect to the locate on of the point to be painted
        m_toolArea.Location = position;

            // Get the area to be painted
        Rectangle areaToPaint = new Rectangle();
        areaToPaint = Rectangle.Intersect(layer.GetRectangle(), m_toolArea);

            // Check this is not a null area
        if (!areaToPaint.IsEmpty)
        {
            // Go through the draw area and set the pixels as they should be
            for (int y = areaToPaint.Top; y < areaToPaint.Bottom; y++)
            {
                for (int x = areaToPaint.Left; x < areaToPaint.Right; x++)
                {
                    layer.GetBitmap().SetPixel(x, y, m_colour);
                }
            }
        }
    }

Vielen Dank für jede Hilfe, die Sie anbieten können.

14voto

Jack Punkte 5610

Sie können die Bitmap-Daten sperren und Zeiger verwenden, um die Werte manuell einzustellen. Das ist viel schneller. Allerdings müssen Sie unsicheren Code verwenden.

public override void PaintPoint(Layer layer, Point position)
    {
        // Rasterise the pencil tool

        // Assume it is square

        // Check the pixel to be set is witin the bounds of the layer

        // Set the tool size rect to the locate on of the point to be painted
        m_toolArea.Location = position;

        // Get the area to be painted
        Rectangle areaToPaint = new Rectangle();
        areaToPaint = Rectangle.Intersect(layer.GetRectangle(), m_toolArea);

        Bitmap bmp;
        BitmapData data = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
        int stride = data.Stride;
        unsafe
        {
            byte* ptr = (byte*)data.Scan0;
            // Check this is not a null area
            if (!areaToPaint.IsEmpty)
            {
                // Go through the draw area and set the pixels as they should be
                for (int y = areaToPaint.Top; y < areaToPaint.Bottom; y++)
                {
                    for (int x = areaToPaint.Left; x < areaToPaint.Right; x++)
                    {
                        // layer.GetBitmap().SetPixel(x, y, m_colour);
                        ptr[(x * 3) + y * stride] = m_colour.B;
                        ptr[(x * 3) + y * stride + 1] = m_colour.G;
                        ptr[(x * 3) + y * stride + 2] = m_colour.R;
                    }
                }
            }
        }
        bmp.UnlockBits(data);
    }

5voto

fixagon Punkte 5416

Dies geschieht mit SetPixel: sperrt das gesamte Bild, setzt das Pixel und gibt es wieder frei

versuchen, das zu tun: Sie erwerben eine Sperre für das gesamte Speicherabbild mit Lockbits, aktualisieren den Prozess und geben die Sperre danach wieder frei.

Lockbits

3voto

CodesInChaos Punkte 103089

Ich verwende normalerweise ein Array, um die rohen Pixeldaten darzustellen. Und dann kopieren zwischen diesem Array und der Bitmap mit unsicherem Code.

Die Herstellung der Reihe von Color ist eine schlechte Idee, da die Color struct ist relativ groß (12 Bytes+). Sie können also entweder Ihre eigene 4-Byte-Struktur definieren (das ist die, die ich gewählt habe) oder einfach ein Array von int o byte .

Sie sollten auch Ihr Array wiederverwenden, da GC auf dem LOH tendenziell teuer ist.

Mein Code ist zu finden unter:

https://github.com/CodesInChaos/ChaosUtil/blob/master/Chaos.Image/

Eine Alternative ist, den gesamten Code mit Zeigern direkt in die Bitmap zu schreiben. Das ist zwar immer noch etwas schneller, kann aber den Code hässlicher und fehleranfälliger machen.

1voto

dowhilefor Punkte 10822

Nur eine Idee: Füllen Sie eine Offscreen-Bitmap mit Ihren Pinselpixeln. Sie müssen diese Bitmap nur neu generieren, wenn der Pinsel, die Größe oder die Farbe geändert wird. Und dann zeichnen Sie diese Bitmap einfach auf Ihre bestehende Bitmap, wo sich die Maus befindet. Wenn Sie eine Bitmap mit einer Farbe modulieren können, können Sie die Pixel in Graustufen setzen und sie mit der aktuellen Pinselfarbe modulieren.

0voto

RichK Punkte 10552

Sie rufen GetBitmap innerhalb Ihrer verschachtelten for-Schleife auf. Es sieht aus wie das nicht notwendig ist, sollten Sie GetBitmap außerhalb der for-Schleifen als der Verweis wird nicht ändern.

Schauen Sie sich auch @fantasticfix Antwort, Lockbits fast immer sortiert langsame Performance-Probleme mit Get / Setting Pixel

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X