Hacker News new | past | comments | ask | show | jobs | submit login

There is actually no perfect solution

For one, browser's can't agree on what CSS solution to pick. Chrome and Safari have `image-rendering: pixelated` whereas Firefox has `image-rendering: crisp-edges`. The spec used to say pixelated meant "keep it looking pixelated" and that "crisp-edges" meant to keep the edges crisp and so allowed various algorithms. The spec was changed in early 2021 so that crisp-edges means "nearest-neighbor" and "pixelated" still means "keep it looking like pixels" which implies nearest-neighbor + extra.

It gets worse though. What is the web developer's intent?

Example: I put

    <img src="128x128.png" style="width: 256px; height: 256px; image-rendering: pixelated">
Seems straight forward right? I want my 128x128 pixel image displayed double size (256x256) where each original pixel is 2x2 pixels on the user's machine.

But, that's not how browsers work. My Windows Desktop has a devicePixelRatio of 1.25 so asking for a 256px by 256px CSS pixel image gives 317x317 device pixels. That means this 128x128 pixel image, scaled to 317x317 with nearest neighbor filtering is going to look really really bad as some pixels get 2x2 and others get 1x1 scaling. This is why `pixelated` = "nearest neighbor" may not actually be what the web designer wants.

Where as, if you manually scale the 128x128 image to say 512x512 using nearest neighbor and then let the browser scale it to 256x256 * devicePixelRatio using the default bilinear filtering it will likely look better.

Note: You can get these non-integer devicePixelRatio values on Chrome or Firefox just by zooming in the browsers (Cmd/Ctrl +/-). On Windows you can also go into the display settings and choose an OS level devicePixelRatio so that OS handled UI widgets and well written apps will adjust how they draw.

See example:

https://jsgist.org/?src=c65dfc4e2ed2e547a2d5aad69360be6d

Zoom in and out and, at least on my machines the right (pre-scaled externally and then scaled down) looks better than the left (original resolution with image-renedering: pixelated




I'm not sure why you're saying there's "no true" definition: there is a spec difference between pixelated and crisp-edges: https://stackoverflow.com/a/25278886


I'm saying it because I've actually read the spec. The spec

https://drafts.csswg.org/css-images/#the-image-rendering

says

> pixelated:

> The image is scaled in a way that preserves the pixelated nature of the original as much as possible, but allows minor smoothing instead of awkward distortion when necessary.

The spec does not say "use nearest neighbor". In fact in specifically allows not using nearest neighbor (end of first sentence). And further, it doesn't specify the algorithm used.

So there is no true definition of what you'll get. There is only a general definition of the intent.


It explicitly says in the crisp-edges section:

> The image must be scaled with the "nearest neighbor" algorithm: treating the original image’s pixel grid as a literal grid of rectangles, scale those to the desired size, then each pixel of the final image takes its color solely from the nearest pixel of the scaled original image.

and then it explicitly says in the pixelated section, right after the summarizing sentence you excerpted:

> For each axis, independently determine the integer multiple of its natural size that puts it closest to the target size and is greater than zero.

> Scale it to this integer-multiple-size as for crisp-edges, then scale it the rest of the way to the target size as for smooth.

The first sentence is descriptive; what is prescribed follows directly after it. I'm not sure how it could do more than that to define "what you'll get".


you're right, the spec changed Feb 2021

https://github.com/w3c/csswg-drafts/issues/6038

That said, my main point stands, Nearest Neighbor looks horrible when you have a non-integer devicePixelRatio which is relatively common. You'll get better results upscaling the image offline and then downscaling in the browser, rather than upscaling in the browser.


Your argument seems to ignore the fact that Apple has their own browser and could put in the time to properly handle pixel-perfect rendering.

Yes there are a lot of complexities with that statement, but I personally think they would have a significant win here if they set a new bar of excellence as a part of the challenge.


I'm only pointing out you'll get better results today, in all browser, by upscaling offline and then letting the browser downscale than upscale in the browser via `image-rendering: pixelated`

The question was "Why are they using image that's been upscaled offline" and I have a reasonable explanation why someone might want to do that.

TL,DR; because it looks better across devices.


> There is actually no perfect solution

There is one: choose the size so that the image is displayed without scaling.


> There is one: choose the size so that the image is displayed without scaling.

You're apparently not paying attention. There is currently no way to do that (except in Chrome via JavaScript maybe)

What size would you choose?

By default a browser will display the image at its natural size * devicePixelRatio. devicePixelRatio can be non-integer values. Further, the devicePixelRatio is fluid in Chrome and Firefox. Press Ctrl/Cmd +/- and it changes.

In Chrome (the only browser that's implemented ResizeObserver with devicePixelContentBoxSize), you can ask exactly how many devicePixels a given element is ... other browsers have not implemented this. And no, elem.getBoundingClientRect will not do it.

But, even if you have the API it would still be a pita. You'd have to take the size of your original image, figure out some integer-multiple that matches the current devicePixelRatio, pray that it still fits your design, create an element that large, then use the ResizeObverser to see if you really got that size. If not, adjust up or down a pixel at time until you do.

"pray that it still fits your design" means assume you had a 200x100 image you want displayed at 2x so 400x200 and the users DPI is 1.33. If you ask for 400x200 you'll actually get 532x266. Your 200x100 image will not scale pixel perfect to 532x266. You could, via JavaScript, compute what size to ask for to get 4x scaling. That size with a DPI of 1.33 is 300.7518796992481. You can't ask for a fractional size (well you can, but it's going to get rounded) But now your design no longer works. You expected the image to take 400 CSS units but it's only taking 300, too small. And, further, there's no guarantee you'll get 300. You might get 301. To find out requires the devicePixelContentBoxSize mentioned above




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: