SetPixel es demasiado lento. ¿Hay una forma más rápida de dibujar en bitmap?

Tengo un pequeño progtwig de pintura en el que estoy trabajando. Estoy usando SetPixel en un bitmap para hacer ese dibujo de líneas. Cuando el tamaño del pincel aumenta, al igual que 25 píxeles de ancho, se produce una notable disminución del rendimiento. Me pregunto si hay una forma más rápida de dibujar en un bitmap. Aquí hay un poco del trasfondo del proyecto:

  • Estoy usando mapas de bits para poder utilizar capas, como en Photoshop o The GIMP.
  • Las líneas se dibujan manualmente porque con el tiempo se usará la presión de la tableta gráfica para modificar el tamaño de la línea en toda su longitud.
  • Las líneas eventualmente deben ser antialias / suavizadas a lo largo de los bordes.

Incluiré mi código de dibujo solo en caso de que sea esto lento y no del bit de Set-Pixel.

Esto está en las ventanas donde ocurre la pintura:

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(); } 

Implementación de MouseMove:

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

Implementación de 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; } } 

Implementación de 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); } } } } 

Muchas gracias por cualquier ayuda que pueda brindar.

    Puede bloquear los datos de bitmap y usar punteros para establecer manualmente los valores. Es mucho más rápido. Aunque deberás usar código inseguro.

     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); } 

    SetPixel hace esto: bloquea la imagen completa, establece el píxel y lo desbloquea

    intente hacer eso: adquiere un candado para toda la imagen de la memoria con los bits de locking, luego actualiza y libera el locking.

    picaduras

    Usualmente uso una matriz para representar los datos brutos de píxeles. Y luego copie entre esa matriz y el bitmap con un código inseguro.

    Hacer la matriz de Color es una mala idea, ya que la Estructura de Color es relativamente grande (12 bytes +). Así que puedes definir tu propia estructura de 4 bytes (esa es la que elegí) o simplemente usar una matriz de int o byte .

    También debe reutilizar su matriz, ya que el GC en el LOH tiende a ser costoso.

    Mi código se puede encontrar en:

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

    Una alternativa es escribir todo tu código usando punteros en el bitmap directamente. Eso es un poco más rápido, pero puede hacer que el código sea más feo y más propenso a errores.

    Solo una idea: llene un bitmap fuera de la pantalla con sus píxeles Brush. Solo necesita regenerar este bitmap cuando se cambie el pincel, el tamaño o el color. Y luego dibuje este bitmap en su bitmap existente, donde se encuentra el mouse. Si puede modular un bitmap con un color, puede establecer los píxeles en escala de grises y modularlos con el color del pincel actual.

    Llamas a GetBitmap dentro de tu ciclo for nested. Parece que no es necesario, debe obtener GetBitmap fuera de los bucles for ya que la referencia no va a cambiar.

    También mire la respuesta de @fantasticfix, Lockbits casi siempre ordena problemas lentos de rendimiento al obtener / configurar píxeles