Image: | Canvas:
Extracted Pixels:

What's going on here?

A checkered image is drawn onto a Canvas to extract the image's colors using getImageData()

The Extracted Pixels should read values alternating between (0,0,0) (black) and (255,0,255) (pink). This currently fails in all major MacOS Browsers (Safari, Chrome, Firefox) on HiDPI displays.

Safari 6.0

Safari on a Retina MacBook Pro will automatically double the canvas size behind the scenes (backingStoreRatio = 2) but pretend it's still the same. When drawing an image to this double size canvas it will get interpolated (blurred). Then, when extracting the pixels from the image using getImageData(), Safari will scale the canvas down again, blurring the image once again. The result is unpredictable pixel puree.

Bug ID: 12310706 / screenshot

Chrome 25

Even though Chrome 25 doesn't support HiDPI for the Canvas element (everything else on the page does) and it supports imageSmoothingEnabled, it is completely ignored for the 2x retina scaling. This makes sense from an implementation standpoint, as the Canvas element essentially get's scaled up after rendering. This explains why the resulting Canvas looks mushed, the pixel data returned from getImageData() is still correct.

The CSS image-rendering property should be supported here.

Bug 134040 / screenshot

Firefox 17

Firefox 17 doesn't support HiDPI at all. The whole page is rendered in normal resolution. Yet, the 2x scaling that MacOS does after rendering the page, still manages to affect the Canvas element somehow. The rendered Canvas looks exactly like it should, but getImageData() returns the wrong result. I have no idea what's going on here. It still works fine on a non-retina display. The planned HiDPI support should fix this. Update: the PNG file had an ICC color profile embeded. I was (wrongfully) under the impression that these were ignored in modern browsers. The result in FF17 is correct.

Bug 780362 / screenshot

Why does it matter?

With this bug, you can't extract the original colors from an image. This is bad for all kinds of applications that want to analyze image data. The effect is most noticable in games and apps that try to present or work with pixel style images, like this scaling algorithm.

How to fix it?

There is a possible solutions to make this all work on Safari 6 again: drawing at half the size to the canvas (in reality that's at full size then) and extracting the real pixels using the Apple's new getImageDataHD().

This solution is extremely ugly to implement and complicates a lot of previously very simple use cases for the Canvas element.

A better solution would be setting the context's imageSmoothingEnabled to false for a doubled backing store or support for the CSS image-rendering property for an undoubled backing store.

Currently, scaling pixel art in a cross browser way is extremely difficult. It's getting easier in Chrome and Firefox, but Safari makes things more complicated yet again. You can read a bit more about this whole issue in my blog.