UprootLabs / poly-flif

:camera: A poly-fill for the FLIF image format.
https://uprootlabs.github.io/poly-flif/
Other
244 stars 11 forks source link

Disable browser interpolation of <canvas> #10

Open FWeinb opened 8 years ago

FWeinb commented 8 years ago

Currently the browser is doing some interpolation to the canvas that will affect the quality of the decoded image and will affect the perception of the flif image format. To disable this across browsers you would need to add:

canvas {
  image-rendering: optimizeSpeed;             /* Older versions of FF          */
  image-rendering: -moz-crisp-edges;          /* FF 6.0+                       */
  image-rendering: -webkit-optimize-contrast; /* Safari                        */
  image-rendering: -o-crisp-edges;            /* OS X & Windows Opera (12.02+) */
  image-rendering: pixelated;                 /* Awesome future-browsers       */
  -ms-interpolation-mode: nearest-neighbor;   /* IE                            */
}

via StackOverflow

hrj commented 8 years ago

Thanks for the heads up. I am not sure this is applicable here though. This code is drawing pixels using putImageData() which is writing pixels directly to the canvas.

Are you suggesting this based on observation, or just as a general precaution?

FWeinb commented 8 years ago

I am on a retina MBP and I can notice the difference. See:

Without pixelated Without pixelated

With pixelated With pixelated

These are 1:1 screenshots from my display (sorry that they are not the same size)

I would say that the pixelated version is much closer to the intent than the blurry one. Is the drawing via putImageData() accounting for the pixel density of the display?

hrj commented 8 years ago

Cool, thanks! I have pushed the changes to the CSS. Can you please refresh the demo page and confirm if it is fixed? I don't notice any difference in my display.

Is the drawing via putImageData() accounting for the pixel density of the display?

Nope.

FWeinb commented 8 years ago

For my eye this looks better but not quite 100% right after playing with it a bit. The problem seams to be that on a retina screen the canvas will draw 4pixel for 1pixel on the canvas. The browser is doing a better job scaling the images, than scaling the canvas.

Maybe something like this could work:

The resulting image should receive the same post processing a normal image is.

hrj commented 8 years ago

but not quite 100% right after playing with it a bit.

How did you compare? Did you try decoding to PNG using the native flif binary?

Your idea of drawing via browser native graphics is nice though. I would do it slightly differently:

hrj commented 8 years ago

@FWeinb Are your screenshots for a truncated FLIF or for the full FLIF? And what are you comparing against (original image / same-sized PNG / same-size JPG)?

blixt commented 8 years ago

Here's a comparison (2_webp_ll) with "0% truncation" vs. "original image" on a MacBook Pro retina (1:2 pixel ratio). I manually upscaled the screenshot with nearest neighbor to make the different more pronounced (and to avoid the effect being reapplied twice since I took the screenshot on a retina screen).

screen shot 2015-10-21 at 11 33 50 am

blixt commented 8 years ago

With image-rendering: pixelated; (Chrome and Firefox) on the #pngImg element, here's the comparison again:

screen shot 2015-10-21 at 11 52 12 am
hrj commented 8 years ago

On Wed, Oct 21, 2015 at 9:19 PM, Blixt notifications@github.com wrote:

I manually upscaled the screenshot with nearest neighbor to make the different more pronounced

​Can you upload it without upscaling? It is quite confusing.​ Thanks!

hrj commented 8 years ago

Here is a longish article explaining the problem and a (partial?) solution. I can't fix this myself because I don't have such an exotic display. PRs welcome.

In my opinion, the best approach might be to create a blob image and use canvas.drawImage(), instead of drawing individual pixels onto the canvas. It would degrade performance for animations (because browser would have to decode the image repeatedly) but I guess it'd be good enough for a poly-fill.

I also hope to design the API such that the decoding of the FLIF and its rendering are two separate functions; allowing independent improvements to them in the future.

hrj commented 8 years ago

For future reference, I found another article.

blixt commented 8 years ago

You don't need to do any of those things, those articles are outdated to work around image rendering. So the thing happening is that your canvas is pixelated (not upsampled), while the PNG is upsampled (not pixelated).

All you need to do is to make the PNG pixelated as well (using #pngImg { image-rendering: pixelated; } plus all the browser-specific versions) and the two will match for both retina and non-retina (as in my second screenshot).

blixt commented 8 years ago

As requested, here are two unchanged screenshots with and without image-rendering: pixelated on the PNG.

screen shot 2015-10-21 at 1 31 46 pm screen shot 2015-10-21 at 1 31 18 pm

hrj commented 8 years ago

All you need to do is to make the PNG pixelated as well

That seems fair for the purpose of comparison. I will do that soon.

hrj commented 8 years ago

I have pushed an update with "pixelation" enabled on the right side comparison view, so that the comparison with FLIF is fair on high-resolution displays. Please let me know if it doesn't work.

I still need to figure out how to handle this as a library (for standalone viewing of FLIFs).

hrj commented 7 years ago

I tested the current demo with a virtual screen having 192 DPI, with Chromium. The current CSS rules are working fine for the demo. Both the left side canvas (containing decoded FLIF pixels) and the right side image (containing decoded PNG or JPEG pixels) appear similarly pixelated on a higher DPI screen.

As for the library, dealing with arbitrary images, either the image or the API would need to specify the original resolution of the image. Then the library could scale the canvas (or underlying image buffer) accordingly, to match the screen resolution.