2010-11-30 10 views
18

Al escribir píxeles en un contexto de lienzo HTML usando putImageData, los valores de píxel no son exactamente los mismos cuando los vuelvo a buscar. He puesto un sample test page mostrando el problema. Reducida, el problema es:¿Por qué HTML Canvas getImageData() no devuelve exactamente los mismos valores que se acaban de establecer?

var id = someContext.getImageData(0,0,1,1); 
id.data[0]=id.data[3]=64; // 25% red, 25% alpha 
id.data[1]=id.data[2]=0; // No blue or green 
someContext.putImageData(id,0,0); 
var newData = someContext.getImageData(0,0,1,1); 
console.log(newData.data[0]); 

En Chrome V8, el valor de rojo regresa como 63; en Firefox v3.6, Safari v5 e IE9 el valor rojo vuelve como 67 (todo en Windows). En OS X, Chrome v7, Safari v5 y Firefox v3.6 también vuelven como 67. ¡Ninguno de ellos vuelve como el valor 64 originalmente establecido!

El uso de setTimeout para retrasar el ajuste y la recuperación no tiene importancia. Cambiar el fondo de la página no hace diferencia. Usar save() y restore() en el contexto (por this unlikely article) no hace ninguna diferencia.

+0

He puesto otra página de prueba que muestra los [efectos de alfa en un conjunto/valor devuelto] (http://phrogz.net/tmp/htmlcanvas_putimagedata2.html). Cuanto más bajo sea el alfa, más variados serán los resultados. Configurando 'R: 5 G: 0 B: 0 A: 1' regresa como' R: 255 G: 0 B: 0 A: 1'. – Phrogz

+0

¿Alguna vez pudo solucionar este problema? – Billy

+0

@Billy No; incluso las especificaciones HTML5 tienen una Nota (ver el enlace en mi comentario a continuación) sobre el hecho de que set/get puede dar como resultado valores diferentes. – Phrogz

Respuesta

15

ImageData se define en HTML5 como no precomplitulado, pero la mayoría de las implementaciones utilizan un búfer de respaldo premultiplicado para acelerar la composición, etc. Esto significa que cuando se escriben datos y luego se leen desde el búfer de respaldo, puede cambiar.

Supongo que Chrome v8 recogió una versión defectuosa del código premultiplying [un] de webkit.org (Se ha roto antes, aunque no recuerdo ninguna ocurrencia reciente, y eso no explica el solo variación de ventanas)

[Editar: ¿podría valer la pena consultar un webkit cada noche en Windows? como la implementación de imagedata no tiene nada específico para la plataforma, se comparte entre todos los navegadores webkit y podría simplemente romperse en compilaciones basadas en MSVC]

+0

Para que quede claro, todos los navegadores tienen 'errores': ninguno de ellos devuelve '64' después de configurar el mismo. Chrome v8b pasa a devolver un valor diferente de los otros navegadores (aunque más cercano al original). He editado la pregunta para resaltar esto. – Phrogz

+1

Gracias por el comentario sobre las especificaciones wrt premultiplicado alfa. Las especificaciones [señalan explícitamente este problema] (http://www.whatwg.org/specs/web-apps/current-work/multipage/the- canvas-element.html), diciendo: _ "Debido a la naturaleza con pérdida de Al convertir a valores de color alfa premultiplicados y desde estos, los píxeles que se han establecido usando 'putImageData()' pueden ser devueltos a un 'getImageData()' equivalente como valores diferentes. "_ – Phrogz

+0

Tengo un recuerdo de que Opera no obtiene esto problema, pero tal vez desde entonces han cambiado su implementación. – olliej

4

Parece un problema de redondeo para mí ...

64/255 = 0.2509... (rounded down to give 0.25) 
0.25 * 255 = 63.75 (rounded down to give 63) 
== OR == 
64/255 = 0.2509... (rounded up to give 0.26) 
0.26 * 255 = 66.3 (rounded up to give 67) 

Recuerde que 255 es el valor máximo, no 256;)

EDIT: Por supuesto, esto no explica por qué la alfa canal se está comportando ...

+0

Puntos de datos interesantes. Supongo que si hubo un '* 100' en el código subyacente antes de un' round() 'o' ceil() 'que podría tener sentido. Hubiera dicho que era poco probable, pero has logrado alcanzar los dos números mágicos de manera semi directa. :) – Phrogz

+0

Una nota extraña, sin embargo: si configura el alfa a '255', el rojo vuelve a aparecer como' 64' como desee. Tal vez hay una tubería premultiply involucrado? – Phrogz

4

La especificación HTML5 alienta a los proveedores de navegadores a usar algo que se llama Premultiplied Alpha. En esencia, esto significa que los píxeles se almacenan en enteros de 32 bits donde cada canal contiene un valor de color de 8 bits. Por motivos de rendimiento, Alpha Premultiplicado es utilizado por los navegadores. Lo que significa es que premultiplica los valores de color basados ​​en el valor alfa.

Aquí hay un ejemplo. Tiene un color tal que los valores para RGB son 128, 64, 67. Ahora, en aras de un rendimiento superior, los valores de color se premultiplicarán por el valor alfa. Por lo tanto, en caso de que el valor alfa sea 16, todos los valores de color se multiplicarán por 16/256 (= 0.0625). En este caso, los valores resultantes para RGB se convierten en 8, 4, 4.1875 (redondeados a 4 porque los valores de color de los píxeles no son flotantes).

El problema aparece cuando haces exactamente lo que estás haciendo aquí; estableciendo datos de color con un valor alfa específico y luego retirando los valores de color reales. El color azul anterior de 4.1875 que se redondeó a 4 se convertirá en 64 en lugar de 67 cuando llame al getImageData().

Es por eso que está viendo todo esto y nunca cambiará a menos que la implementación subyacente en un motor de navegador cambie para usar un sistema de color que no sufra de esto.

+1

¿Podría citar dónde las especificaciones HTML 5 alientan esto? Veo que las especificaciones que requieren que todos los imagedata obtengan/configuren usan ** un ** premultiplicado alfa, y veo la nota (que cité arriba) donde dicen lo que podría ocurrir _si_ la implementación pasa a usar premultiplicado. – Phrogz

+0

@Phrogz ¿qué especificación era esa? – NoBugs

+0

@NoBugs El que he vinculado en mi comentario sobre la respuesta de olliej. – Phrogz

Cuestiones relacionadas