La lectura de píxeles de imágenes en JavaScript devuelve resultados inesperados para píxeles semitransparentes

Estoy leyendo datos RGBA de un archivo png en JavaScript. Para hacer esto, dibujo la imagen en un canvas y uso .getImageData . Los datos difieren de lo que espero:

Imagen de prueba: http://sofes.miximages.com/canvas/transparent.png

El RGBA del tercer píxel en JS es: [9, 0, 0, 54] (probado en chrome y firefox) pero debería ser [12, 0, 0, 54] (al menos es lo que afirman las almohadas gimp y python).

Mi conjetura lo que podría estar mal

  1. drawImage hace un poco de composición (establecer ctx.globalCompositeOperation para copy no hace ninguna diferencia)
  2. Hay algo de color en el tiempo de carga de la imagen.

Código de ejemplo

  1. http://codepen.io/anon/pen/EaemZw
  2. Seleccione la imagen de prueba
  3. comprobar la salida de la consola

1) Composición

Cuantas más operaciones de dibujo se apliquen, más errores de redondeo ocurrirán con el tiempo. Sin embargo, con un empate inicial esto no importará.

copy modo de copy no lo afecta como lo descubriste. Esto es simplemente ignorar los datos de fondo existentes. source-over usa el valor alfa del fondo, pero como inicialmente no hay ninguno, tendrá el mismo efecto.

Sin embargo, el problema principal con esto es que hay problemas generales con los errores alfa y de redondeo debido a los valores alfa previamente multiplicados que deben convertirse internamente.

2) gestión del color

Los navegadores admiten varios niveles de gestión del color. Esto incluye los siguientes datos del archivo PNG:

  • Fragmento sRGB que indica cómo el PNG se ajusta al estándar sRGB (relativo, absoluto, etc.)
  • El fragmento iCCP que contiene un perfil ICC que incluye gamma, que si el navegador es compatible con ICC y también esta versión de ICC (las versiones típicas son las versiones 2 y 4).
  • Si no se encuentra ningún perfil sRGB o iCCP, buscará el fragmento gAMA que contiene un número que representa el valor gamma.

Cuando se apliquen estos datos, alterará el bitmap original. Gamma no afectará los valores más bajos ni los más altos, pero el rango medio tendrá un cambio notable. Los valores que todavía están alterados se modificarán debido a la gestión del color y los errores de conversión (1.).

Por ejemplo, incluso si el archivo no tiene un fragmento de gamma (o un “gamma de archivo”), que es el caso de su archivo de prueba, o su valor es 1, se seguirá aplicando un gamma de pantalla (excepto en IE). Para Windows esto sería 2.2 y para Mac típicamente 1.8.

Y esto sucede antes de que la imagen vuelva a utilizarse en el código con el canvas.

Alternativamente, sugeriría echar un vistazo a mi pngtoy (es gratis / MIT) que creé para este tipo de escenario, para permitir que los desarrolladores obtengan un bitmap sin alterar de un PNG (solo vea las notas / el estado ya que todavía está en alfa ). A continuación, proporcioné un ejemplo de código que también lee el tercer píxel que da 12 para el canal rojo como se esperaba.

Prueba usando pngtoy

Esto mostrará el valor de bitmap sin alterar sin procesar.

 var out = document.querySelector("output"), png = new PngToy(); png.fetch("http://sofes.miximages.com/canvas/oX1kom7.png").then( function() {png.decode().then(show)} ); function show(bmp) { var data = bmp.bitmap; out.innerHTML += data[8] + "," + data[9] + "," + data[10] + "," + data[11]; } 
   Loading external scripts (pngtoy.min.js + Promise polyfill for IE...)

Su conjetura # 2 es correcta.

Los navegadores tienen la libertad dada por la especificación para hacer ajustes de Alpha y Gamma a las imágenes entrantes.