javascript - tainted - putimagedata
¿Por qué HTML Canvas getImageData() no devuelve exactamente los mismos valores que se acaban de establecer? (3)
ImageData se define en HTML5 como no premultiplicado, pero la mayoría de las implementaciones canvas utilizan un búfer de respaldo premultiplicado para acelerar la composición, etc. Esto significa que cuando los datos se escriben y luego se leen desde el búfer de respaldo, puede cambiar.
Supongo que Chrome v8 eligió una versión defectuosa del código premultiplicado [un] de webkit.org (Se ha roto antes, aunque no recuerdo ninguna ocurrencia reciente, y eso no explica la varianza solo de Windows)
[edit: ¿podría valer la pena revisar un webkit cada noche en Windows? como la implementación de los datos de imagen 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]
Al escribir píxeles en un contexto de lienzo HTML usando putImageData
, encuentro que los valores de píxel no son exactamente los mismos cuando los vuelvo a buscar. He puesto una página de prueba de muestra que muestra el problema. Hervido, el problema es:
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 rojo vuelve a ser 63
; en Firefox v3.6, Safari v5 e IE9, el valor rojo vuelve a ser 67
(todo en Windows). En OS X, Chrome v7, Safari v5 y Firefox v3.6 también regresan como 67
. ¡Ninguno de ellos vuelve como el valor 64
originalmente establecido!
El uso de setTimeout
para demorar entre la configuración y la recuperación no tiene importancia. Cambiar el fondo de la página no hace diferencia. Usar save()
y restore()
en el contexto (según este artículo improbable ) no hace diferencia.
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;)
EDITAR: Por supuesto, esto no explicaría por qué el canal alfa se está comportando ...
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. Entonces, 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
lugar de 67
cuando llame a 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.