Using nearest-neighbor with CSS zoom on canvas (and img)

Matt Greer picture Matt Greer · Nov 6, 2011 · Viewed 10.6k times · Source

When using the CSS zoom property, how can I convince the browser to use "nearest neighbor", instead of "bilinear" or any other more advanced zooming algorithms?

My setup is a div that contains a canvas, and the div gets its zoom set via JavaScript to be <div style="zoom:3200%">...</div> and to get nearest neighbor, I am using image-rendering: -webkit-optimize-contrast in my CSS. The app is available here ('z' zooms in, 'shift-z' zooms back out), and my css is here

Here is the desired effect in Chrome on OSX (zoom is set to 3200%): zoom on OSX/Chrome using nearest neighbor

But here is the same thing in Chrome on Windows 7: zoom on Windows/Chrome not using nearest neighbor

In both cases it is "vanilla" Chrome (version 15.x.x) out of the box, no experimental flags are turned on.

How can I convince Chrome on Windows to use nearest neighbor? For that matter, how can I convince all browsers? Safari also does not use nearest neighbor (so far the app only works in WebKit based browsers)

The CSS image-rendering property does affect Chrome/OSX and gives me the desired effect. But Chrome/Windows and Safari(5.1)/OSX both seem to completely ignore it. Something tells me I'm just out of luck, but I figured I'd ask.

Using zoom on the div container is so simple and works beautifully in Chrome/OSX, if I must resort to scaling my canvases internally, I can do that too. But would prefer the literally one line of code solution if possible!

UPDATE: I have found the use of image-rendering: optimizeSpeed helps. However it seems finicky in Chrome/Windows. If I set it on too many elements (I initially tried, my containers and all canvases), it doesn't take effect. But if I apply it to just canvas, I get 98% of the way there.

Now my problem is the first time I draw while zoomed in, it works perfectly, all other subsequent drawing actions are fuzzy while they are taking place, then revert to the correct nearest-neighbor afterwards (my app draws into a scratch canvas first, then applies the drawing to the real canvas). There is something odd about the scratch canvas where Chrome insists on using bilinear. I think with some digging I can resolve that.

UPDATE2: It seems like image-rendering on Chrome/Windows just isn't implemented well and is a bit buggy. I can now confirm that the values optimizeSpeed and optimizeQuality are not supported on Chrome/Windows. If you set image-rendering to them, Chrome will ignore the set. Chrome/Windows does recognize -webkit-optimize-contrast, however it does not use it consistently. Chrome will flip between what looks to be a bilinear scaling algorithm and nearest-neighbor almost at random. I've not been able to consistently get Chrome to use nearest-neighbor.

I tried a build of Chromium 17 on Windows and it behaves the same way.

Firefox (8.0.1) is looking pretty promising though as it does seem to honor -moz-crisp-edges quite well. Originally I was targeting Chrome as my "ideal browser" for this app, I might just switch over to Firefox.

In the end, it seems like proper support for image-rendering is in the pipeline for Chrome, just not quite there yet. WebKit itself claims to fully support all image-rendering values, but I'm guessing the build of WebKit that Chrome uses hasn't quite updated to this new fix.

Answer

Timothy Miller picture Timothy Miller · Dec 27, 2011

There is a way. I asked a question similar to this over here: How to stretch images with no antialiasing and got a really good response. I've modified it quite a bit since then, but here's a simple version which stretches (w/out anti-aliasing) when clicked: http://jsfiddle.net/howlermiller/U2eBZ/1/

It's incredibly hackish though. Don't use it unless you must. It has to re-create the image every time you click on it, so with a big image it would be slow and terrible. But it does work on Chrome/Windows.

Edit: This is supported in the CSS3 spec now! Support is super spotty at the moment, but Chrome just added support so I'm hopeful that we'll be able to use it before too long. Good times!

.thing {
  /* IE, only works on <img> tags */
  -ms-interpolation-mode: nearest-neighbor;
  /* Firefox */
  image-rendering: crisp-edges;
  /* Chromium + Safari */
  image-rendering: pixelated;
}

Demo