Tengo un pequeño programa de pintura que estoy trabajando. Estoy usando SetPixel en un mapa de bits para hacer que el dibujo de líneas. Cuando el tamaño del pincel se hace grande, el 25 píxeles en el hay una notable disminución del rendimiento. Me pregunto si hay una manera más rápida de dibujar un mapa de bits. Aquí es un poco de los antecedentes del proyecto:

  • Estoy usando mapas de bits, de modo que puedo utilizar capas, como en el Photoshop o El GIMP.
  • Líneas se han dibujado manualmente ya que eventualmente el uso de las tabletas gráficas de presión para modificar el tamaño de la línea de su longitud.
  • Las líneas, finalmente, deben ser anti-aliaced/alisado a lo largo de los bordes.

Voy a incluir mi código de dibujo sólo en caso de que se esto que es lento y no el Conjunto de Píxeles de bits.

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 pueda ofrecer.

OriginalEl autor Pyro | 2011-10-14

5 Comentarios

  1. 12

    Puede bloquear el mapa de bits de datos y el uso de punteros para ajustar manualmente los valores. Es mucho más rápido. Aunque usted tendrá que 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);
        }
    Gracias por este. Creo que puedo ver lo que está pasando; ptr es un trozo de los datos de mapa de bits que contiene el rojo, verde y azul los valores de los píxeles y configuración de forma manual. Voy a dar a este un ir hoy y ver cómo funciona. Muchas gracias por esto, me siento en mi entendimiento ha avanzado a otro nivel 😛 Solo una cosa, yo pensaba que un mapa de bits en C# tiene un canal alfa. Pensé que esto sería manejado en los píxeles de color de la información junto con el RGB.
    Es necesario especificar el msdn.microsoft.com/en-us/library/… para incluir un componente alfa. Acaba de cambiar PixelFormat.Format24bppRgb a PixelFormat.Format32bppArgb. Creo que debe ser después de R por lo que sólo el desplazamiento por +3. Si usted hace uso de 32 bits por píxel, usted tendrá que cambiar x * 3 a x * 4 ya que va a ser de 4 bytes por píxel ahora.
    Acabo de probar esto. Es hermoso y rápido ahora en todos los tamaños de pincel! Sin embargo ahora hay algún comportamiento extraño: El dibujo real que sucede en un desplazamiento a donde está el ratón. Me pregunto si nada que ver con esta línea aquí: BitmapData de datos = bmp.LockBits(new Rectangle(areaToPaint.A la derecha, areaToPaint.La Parte Inferior), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb); debe ser una posición especificada por el rectángulo. He utilizado suministrado ‘posición’ de valor. Es ese derecho?
    El position valor para el rectángulo es la posición del rectángulo en el picturebox o panel. Estás usando la posición del ratón. Si el diseño de la imagen no está centrada, sólo se puede utilizar (0, 0), para la posición. Si se centra y su picturebox es más grande que el de la imagen, entonces usted necesitará ajustar la compensación. También debe cambiar new Rectangle(areaToPaint.Right, areaToPaint.Bottom) a new Rectangle(0, 0, bmp.Width, bmp.Height). Pensé areaToPaint fue de las dimensiones de la imagen completa.
    Es necesario el uso de código inseguro para hacer esto? No podía utilizar Marshal.WriteByte escribir a la dirección de memoria de la IntPtr en vez de convertirla en una byte* hacer inseguro operaciones de puntero? O tendría que ser más lento?

    OriginalEl autor Jack

  2. 5

    SetPixel hace esto:
    se bloquea la imagen entera, establece el píxel y la abre

    tratar de hacer eso: adquirir un bloqueo para el conjunto de la imagen de memoria con lockbits, proceso de actualizar y liberar el bloqueo después.

    lockbits

    Así que me bloqueo el mapa de bits, hacer mi código de dibujo y, a continuación, desbloquear?
    yo te he puesto un ejemplo, de un momento…
    3 años más tarde, todavía no hay ningún ejemplo
    Es sólo un momento
    SetPixel != LockBits

    OriginalEl autor fixagon

  3. 3

    Yo suelo usar una matriz para representar los píxeles en bruto de datos. Y, a continuación, copie entre la matriz y el mapa de bits con código inseguro.

    De decisiones de la matriz de Color es una mala idea, ya que la Color estructura es relativamente grande(12 bytes+). Así que usted puede definir su propio 4 bytes struct(que es el que yo elegí) o simplemente utilizar una matriz de int o byte.

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

    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 aún, pero puede hacer que el código sea más feo y más propenso a errores.

    ¿El uso de una matriz del tamaño del mapa de bits o sólo el tamaño de la zona que va a ser modificado?
    En mi aplicación que utiliza una matriz con el mismo tamaño que el de mapa de bits, pero yo necesitaba para trabajar en el conjunto de mapa de bits. Pero incluso si me gustaría trabajar sólo en una parte del mapa de bits estaría todavía el uso de la matriz completa, pero posiblemente sólo de relleno en la parte de lo que necesito. De esa manera no tengo que asignar una nueva matriz cuando se trabaja en una manera diferente de tamaño de la sección de la imagen.

    OriginalEl autor CodesInChaos

  4. 1

    Sólo una idea: Llenar un fuera de la pantalla de mapa de bits con su Cepillo de píxeles. Usted sólo necesita regenerar este mapa de bits cuando el pincel, el tamaño o el color cambia. Y, a continuación, simplemente dibujar este mapa de bits en su actual mapa de bits, donde el ratón se encuentra.
    Si usted puede modular un mapa de bits con un color, usted puede ajustar los píxeles en escala de grises y modular con el actual color de pincel.

    Eso suena como una idea genial. Gracias. Supongo que si yo uso la transparencia, a continuación, iba a trabajar cuando dibujo de las líneas existentes.
    O tal vez mantener una Matriz de Bitmaps de cada pincel (de color blanco), y hacer que el color de modulación (si es necesario) como usted ha mencionado 🙂 (sí soy demasiado tarde para este post)

    OriginalEl autor dowhilefor

  5. 0

    Estás llamando GetBitmap dentro de su bucle for anidado. Parece que no es necesario, usted debe GetBitmap fuera de los bucles for como la referencia no va a cambiar.

    También mira @fantasticfix respuesta, Lockbits casi siempre de tipo lento, problemas de rendimiento con la guía/ajuste de píxeles

    OriginalEl autor RichK

Dejar respuesta

Please enter your comment!
Please enter your name here