2011-10-14 22 views
11

Tengo un pequeño programa de pintura en el que estoy trabajando. Estoy usando SetPixel en un mapa de bits 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 mapa de bits. Aquí hay un poco del trasfondo del proyecto:SetPixel es demasiado lento. ¿Hay una forma más rápida de dibujar en mapa de bits?

  • 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 deberían eventualmente estar antialiadas/suavizadas a lo largo de los bordes.

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

Esto es en las ventanas donde la pintura sucede:

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 puede proporcionar.

Respuesta

11

Puede bloquear los datos de mapa de bits y punteros utilizar para configurar 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); 
    } 
+0

Gracias por esto. Creo que veo lo que está pasando; ptr es un conjunto de datos de mapa de bits que contiene los valores de píxel rojo, verde y azul y los configura manualmente. Voy a probar esto hoy y ver cómo funciona. Muchas gracias por esto, siento que mi entendimiento ha avanzado en otro nivel: P Sin embargo, solo una cosa, pensé que un mapa de bits en C# también tiene un canal alfa. Pensé que esto se manejaría en la información de color del píxel junto con el RGB. – Pyro

+1

Debe especificar http://msdn.microsoft.com/en-us/library/system.drawing.imaging.pixelformat.aspx para incluir un componente alfa. Simplemente cambie 'PixelFormat.Format24bppRgb' a' PixelFormat.Format32bppArgb'. Creo que debería ser después de R, así que solo lo compensé con +3. Si usa 32 bits por píxel, necesitará cambiar 'x * 3' a' x * 4' ya que ahora será de 4 bytes por píxel. – Jack

+0

Acabo de probar esto. ¡Ahora es encantador y rápido en todos los tamaños de pincel! Sin embargo, ahora hay algún comportamiento extraño: el dibujo real ocurre en un desplazamiento hacia donde se encuentra el mouse. Me pregunto si tiene algo que ver con esta línea aquí: BitmapData data = bmp.LockBits (new Rectangle (areaToPaint.Right, areaToPaint.Bottom), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb); Tiene que haber una posición especificada para el rectángulo. Utilicé el valor 'posición' suministrado. ¿Está bien? – Pyro

0

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

+0

Eso suena como una idea genial. Gracias. Supongo que si uso transparencia, entonces funcionaría al dibujar líneas existentes. – Pyro

5

SetPixel hace esto: es encierra toda la imagen, fija el pixel y lo desbloquea

intento de hacer eso: se adquiere un bloqueo de la imagen de memoria entera con LockBits, procesar actualizar y liberar el bloqueo después de .

lockbits

+0

Así que bloqueo el mapa de bits, hago mi código de dibujo y luego lo desbloqueo? – Pyro

+0

te hago un ejemplo, un momento ... – fixagon

+18

3 años después, aún no hay ningún ejemplo –

3

por lo general utilizar una matriz para representar los datos de píxeles primas. Y luego copie entre esa matriz y el mapa de bits con un código inseguro.

Hacer la matriz de Color es una mala idea, ya que la estructura 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 volver a utilizar 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 el código usando punteros en el mapa de bits 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.

+1

¿Utiliza una matriz del tamaño del mapa de bits objetivo o solo el tamaño del área que se va a modificar? – Pyro

+0

En mi aplicación utilicé una matriz con el mismo tamaño que el mapa de bits, pero necesitaba trabajar en todo el mapa de bits. Pero incluso si solo trabajara en una parte del mapa de bits, igual usaría la matriz completa, pero posiblemente solo complete la parte que necesito. De esta forma, no es necesario que asigne una nueva matriz cuando trabaje en una sección de tamaño diferente de la imagen. – CodesInChaos

0

Está llamando a GetBitmap dentro de su ciclo for anidado. Parece que no es necesario, debe obtener GetBitmap fuera de los bucles for ya que la referencia no va a cambiar.

También se fijan en @fantasticfix respuesta, LockBits casi siempre clasifica los problemas de rendimiento lento con obtener/establecer píxeles

Cuestiones relacionadas