ejemplo c# graphics bitmap gdi layer

c# - ejemplo - SetPixel es demasiado lento. ¿Hay una forma más rápida de dibujar en mapa de bits?



bitmap c# (5)

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 bloqueo, luego actualiza y libera el bloqueo.

picaduras

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:

  • 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.


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.


Usualmente uso una matriz para representar los datos brutos de píxeles. 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 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 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.


Puede bloquear los datos del mapa de bits 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); }


Llamas a GetBitmap dentro de tu 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 mire la respuesta de @fantasticfix, Lockbits casi siempre ordena problemas lentos de rendimiento al obtener / configurar píxeles