poly-flif icon indicating copy to clipboard operation
poly-flif copied to clipboard

Disable browser interpolation of <canvas>

Open FWeinb opened this issue 8 years ago • 16 comments

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

FWeinb avatar Oct 15 '15 10:10 FWeinb

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?

hrj avatar Oct 15 '15 11:10 hrj

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?

FWeinb avatar Oct 15 '15 11:10 FWeinb

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.

hrj avatar Oct 15 '15 11:10 hrj

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:

  • Draw the FLIF to a canvas via putImageData()
  • Get a PNG from the canvas via toDataURL("image/png");
  • Draw that PNG via a <img>-Tag

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

FWeinb avatar Oct 15 '15 12:10 FWeinb

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:

  • After decoding the FLIF, encode to BMP format (it's mostly just a pixel array with some headers).
  • Get a blob URL for the BMP
  • Draw either on the canvas, or via img tag. Advantage of canvas is that we can support animations properly.

hrj avatar Oct 15 '15 12:10 hrj

@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)?

hrj avatar Oct 16 '15 15:10 hrj

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 avatar Oct 21 '15 15:10 blixt

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

blixt avatar Oct 21 '15 15:10 blixt

On Wed, Oct 21, 2015 at 9:19 PM, Blixt [email protected] 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 avatar Oct 21 '15 15:10 hrj

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 avatar Oct 21 '15 16:10 hrj

For future reference, I found another article.

hrj avatar Oct 21 '15 16:10 hrj

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 avatar Oct 21 '15 17:10 blixt

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

blixt avatar Oct 21 '15 17:10 blixt

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 avatar Oct 21 '15 18:10 hrj

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 avatar Oct 25 '15 08:10 hrj

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.

hrj avatar Apr 30 '17 12:04 hrj