1

I am reading RGBA data from a png file in JavaScript. To do this I draw the image on a canvas and use .getImageData. The data differs from what I expect:

Test image: https://raw.githubusercontent.com/FlorianLudwig/imghash/master/test/transparent.png

The third pixel's RGBA in JS is: [9, 0, 0, 54] (tested in chrome and firefox) but it should be [12, 0, 0, 54] (at least it is what gimp and python pillow claim).

My guesses what could be wrong

  1. drawImage does some compositing (setting ctx.globalCompositeOperation to copy does not make any difference)
  2. there is some color mangament going on at load time of the image

Example Code

  1. http://codepen.io/anon/pen/EaemZw
  2. Select the test image
  3. check console output
FlorianLudwig
  • 325
  • 2
  • 9

2 Answers2

1

Your guess#2 is correct.

Browsers are given freedom by the spec to do Alpha & Gamma adjustments to incoming images.

markE
  • 102,905
  • 11
  • 164
  • 176
  • Do you have any evidence or sources to support this? Any idea how to work around it? – FlorianLudwig Mar 07 '15 at 17:27
  • 1
    Sure. The WhatWG spec relating to color correction is here: https://html.spec.whatwg.org/multipage/scripting.html#colour-spaces-and-colour-correction. And here is a prior SO question dealing with reducing color correction of images done by browsers: http://stackoverflow.com/questions/23497925/how-can-i-stop-the-alpha-premultiplication-with-canvas-imagedata/23501676#23501676. Good luck with your project! – markE Mar 07 '15 at 17:40
1

1) Compositing

The more draw operations applied the more rounding errors will occur over time. However, with an initial draw this won't matter.

copy mode doesn't affect it as you discovered. This is just ignoring the existing background data. source-over do use alpha value of background, but as there is none initially it will have the same effect.

The main problem with this though is that there is general problems with alpha and rounding errors due to pre-multiplied alpha values which needs to be converted internally.

2) Color management

Browsers do support various levels of color management. This includes the following data from the PNG file:

  • sRGB chunk stating how the PNG conforms to the sRGB standard (relative, absolute etc.)
  • iCCP chunk which contains an ICC profile including gamma, that if the browser support ICC and also this version of ICC (typical versions are version 2 and 4).
  • If no sRGB or iCCP profile are found, it will look for gAMA chunk which contains a number representing the gamma.

When these data are applied it will alter the original bitmap. Gamma will not affect the lowest nor the highest values, but the mid-range will have a noticeable change. Values that are still altered will be altered due to color management and conversion errors (1.).

For example, even if the file does not have a gamma chunk (or a "file gamma"), which is the case for your test file, or its value is 1, a display gamma will still be applied (except in IE). For windows this would be 2.2 and for Mac typically 1.8.

And this happens before the image is returned to be used in the code with canvas.

Alternatively I would suggest taking a look at my pngtoy (it's free/MIT) which I created for this type of scenario, to enable developers to get a raw unaltered bitmap from a PNG (just see notes/status as it is currently still in alpha). I supplied a code example below which also reads the third pixel giving 12 for red channel as expected.

Test using pngtoy

This will show the raw unaltered bitmap value.

var out = document.querySelector("output"),
    png = new PngToy();

png.fetch("https://i.imgur.com/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];
}
<script src="https://rawgit.com/epistemex/pngtoy/master/tests/Promise.js"></script>
<script src="https://rawgit.com/epistemex/pngtoy/master/pngtoy.min.js"></script>
<output>Loading external scripts (pngtoy.min.js + Promise polyfill for IE...)<br></output>
  • Is it possible to always get the bitmap data in RGBA? (and not rgba/rgb/indexed) – FlorianLudwig Mar 10 '15 at 11:56
  • @FlorianLudwig certainly, you can use the `convertToRGBA(bmp, options).then(...)` (bmp from decode()). Use options to f.x. turn off gamma correction (`useGamma: false`). Passes in an object where RGBA buffer is on the bitmap property in same format as with canvas. –  Mar 10 '15 at 13:53
  • The link to pngtoy is broken. Is the library still available? – Robert Graves Oct 22 '15 at 14:02