Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

HTML Canvas inaccurately sets pixel color when alpha is lower than one

Please see this example: http://codepen.io/anon/pen/QNGzBP

const canvas = document.getElementById('canvas')
const ctx = canvas.getContext('2d')

// set pixel at 0,0 to rgba(2, 0, 255, 0.2)
const imageData = ctx.getImageData(0, 0, 1, 1)
imageData.data[0] = 2
imageData.data[1] = 0
imageData.data[2] = 255
imageData.data[3] = 0.2 * 255 // 0.2 opacity
ctx.putImageData(imageData, 0, 0, 0, 0, 1, 1)
console.log('Setting pixel 0,0 to', {
  r: imageData.data[0],
  g: imageData.data[1],
  b: imageData.data[2],
  a: imageData.data[3] / 255
})

// retrieve pixel at 0,0
const newImageData = ctx.getImageData(0, 0, 1, 1)
console.log('Fetching pixel at 0,0', {
  r: newImageData.data[0],
  g: newImageData.data[1],
  b: newImageData.data[2],
  a: newImageData.data[3] / 255
})

The above code modifies a single pixel on the canvas, and then retrieves it, while console logging the process. The canvas seems to mutate the RGB data when the alpha is lower than 1.

It seems to occur both in Chrome and Firefox. Is this simply a browser bug? I'm producing a PNG out of the canvas, and require the colors to be 100% accurate. Is there any workaround?

Edit:

http://codepen.io/anon/pen/ZWLbKx

More tests to show how the alpha mutates the rgb values.

like image 924
J Doe Avatar asked Oct 18 '22 13:10

J Doe


1 Answers

Very sorry to disappoint, but the HTML5 canvas has been ratified as "lossy": it uses alpha-channel premultiplication.

From https://www.w3.org/TR/2dcontext/#dom-context-2d-getimagedata (see 2nd "Note:" box):

Due to the lossy nature of converting to and from premultiplied alpha color values, pixels that have just been set using putImageData() might be returned to an equivalent getImageData() as different values.

  • Recommended reading: How can I stop the alpha-premultiplication with canvas imageData?

  • Also relevant: Why function returns wrong color in canvas?

  • Seems vaguely similar but might be regarding a totally different domain: Invalid blending results across all browsers with HTML5 canvas

...There are lots of JavaScript PNG encoders/decoders. Unfortunately that's where it's at, as of 2017.

FWIW, I've had a go at bumping the r/g/b/a values up and down to try and make the canvas spit out the values I actually want. 5 minutes of experimenting shows that it "hops" over the specific pixel value you want depending on what the alpha is set to.

like image 131
i336_ Avatar answered Oct 21 '22 04:10

i336_