194 votos

¿' Es la mejor manera de establecer un solo píxel en un lienzo de HTML5?

El HTML5 Canvas no tiene un método para establecer explícitamente un solo píxel.

Podría ser posible establecer un píxel usando una línea muy corta, pero luego los casquillos antialising y línea podrían interferir.

Otra manera podría ser crear un pequeño ImageData objeto y uso:

context.putImageData(data, x, y)

para ponerlo en su lugar.

¿Nadie puede describir una forma eficiente y confiable de hacer esto?

301voto

Phrogz Puntos 112337

Existen dos mejores contendientes:

  1. Crear un 1x1 datos de la imagen, ajustar el color, y putImageData a la ubicación:

    var id = myContext.createImageData(1,1); // only do this once per page
    var d  = id.data;                        // only do this once per page
    d[0]   = r;
    d[1]   = g;
    d[2]   = b;
    d[3]   = a;
    myContext.putImageData( id, x, y );     
    
  2. Uso fillRect() para dibujar un pixel (no debe haber problemas de aliasing):

    ctx.fillStyle = "rgba("+r+","+g+","+b+","+(a/255)+")";
    ctx.fillRect( x, y, 1, 1 );
    

Usted puede probar la velocidad de estos de aquí: http://jsperf.com/setting-canvas-pixel
En Chrome el 1x1 de los datos de la imagen es aproximadamente 10 veces(!) tan rápido como el uso de fillRect.
En Firefox 3.6 el 1x1 es de sólo 1.3 x más rápido.
En Firefox 4.0 b el 1x1 es acerca de 80x(!!) más lento que fillRect.
Esta desaceleración se produce sólo cuando la aceleración de hardware está habilitada y puede ser fijo.

Yo recomiendo usar el 1x1 de los datos de la imagen para obtener la máxima velocidad.

Otros, más absurda de las alternativas son:

  • utilizando getImageData()/putImageData() sobre la totalidad del lienzo; como se muestra en las pruebas, esto es alrededor de 100 veces más lento que el de otras opciones.

  • la creación de una imagen personalizada mediante una url de datos y utilizando drawImage() a mostrar:

    var img = new Image;
    img.src = "data:image/png;base64," + myPNGEncoder(r,g,b,a);
    // Writing the PNGEncoder is left as an exercise for the reader
    
  • la creación de otro img o de lona llena de todos los píxeles que desee y utilice drawImage() fundir sólo los píxeles que desee a través de. Esto sería, probablemente, muy rápido, pero tiene la limitación de que usted necesita para pre-calcular los píxeles que necesitan.

Tenga en cuenta que mis pruebas no en el intento de salvar y restaurar el lienzo contexto fillStyle; esto además de ralentizar la fillRect de rendimiento. También tenga en cuenta que (debido a las limitaciones en el marco de pruebas) no estoy comenzando con una pizarra limpia o pruebas exactamente el mismo conjunto de pixeles para cada prueba.

19voto

Alnitak Puntos 143355

Yo no había considerado fillRect(), pero las respuestas me rechazaron de referencia contra putImage().

Poniendo de 100.000 pixeles coloreados al azar en lugares al azar, con Chrome 9.0.597.84 en un (viejo) MacBook Pro, lleva menos de 100ms con putImage(), pero casi 900ms utilizando fillRect(). (Código de referencia en http://pastebin.com/4ijVKJcC).

Si en lugar de elegir un solo color fuera de los bucles y sólo la parcela de que el color en lugares al azar, putImage() toma 59ms vs 102ms para fillRect().

Parece que la sobrecarga de la generación y análisis de un CSS color de la especificación en rgb(...) de la sintaxis es la responsable de la mayor parte de la diferencia.

Poner raw RGB los valores directamente en un ImageData bloque en el otro lado no requiere de manejo de cadenas de caracteres o de análisis.

7voto

Daniel Puntos 21

¿Desde distintos navegadores parece preferir métodos diferentes, tal vez tendría sentido hacer una prueba más pequeña con los tres métodos como parte del proceso de carga para averiguar cuál es el mejor usar y luego usar eso en toda la aplicación?

4voto

sdleihssirhc Puntos 18791

¿Qué tal un rectángulo? Eso tiene que ser más eficiente que la creación de un ImageData objeto.

1voto

trusktr Puntos 4518

¿Podrías también hacer una amplia línea de 1 píxel con una longitud de 1 píxel y mover su dirección a lo largo de un solo eje.

            ctx.lineWidth = 1; // one pixel wide
            ctx.strokeStyle = rgba(...);
            ctx.moveTo(50,25); // positioned at 50,25
            ctx.lineTo(51,25); // one pixel long
            ctx.stroke();

Iteramos.com

Iteramos es una comunidad de desarrolladores que busca expandir el conocimiento de la programación mas allá del inglés.
Tenemos una gran cantidad de contenido, y también puedes hacer tus propias preguntas o resolver las de los demás.

Powered by:

X