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%):
But here is the same thing in Chrome on Windows 7:
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.
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;
}