Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Improving image quality of CSS-downscaled elements

I'm zooming out the contents of my awesome graph editor using transform: scale(x). When zooming-out the scale goes down towards 0 (exclusive), when zooming-in the scale goes up, up to a maximum of 1 (inclusive) which means full zoom or initial scale.

However, when zoomed-out considerably, image quality starts becoming really noisy -- please consider the following example, and notice how zooming out will make image appearance noisy:

var graphContainer = document.getElementById("graph-container");
var zoomInButton = document.getElementById("zoom-in-button");
var zoomOutButton = document.getElementById("zoom-out-button");
var zoomLevel = 1;

zoomInButton.addEventListener("click", function () {
    zoomLevel = Math.max(1, zoomLevel - 0.25);
    graphContainer.style.transform = "scale(" + (1 / zoomLevel) + ")";
});

zoomOutButton.addEventListener("click", function () {
    zoomLevel = zoomLevel + 0.25;
    graphContainer.style.transform = "scale(" + (1 / zoomLevel) + ")";
});
#editor-container {
    background-color: #001723;
}

#graph-container { transform-origin: top center; }
<div id="editor-container">
    <button id="zoom-in-button">Zoom in</button>
    <button id="zoom-out-button">Zoom out</button>
    <div id="graph-container">
        <img class="shape" src="http://i.imgur.com/zsWkcGz.png" />
    </div>
</div>

Demo also on JSFiddle

This image is a canvas-drawn shape that interactively visualizes a connection between two graph nodes, exported into png.


Please zoom out and see how noisy that line is, even though zooming is done in steps of 0.25 and with CSS. How can I get rid of this pixel-noise? The issue happens in both the latest Google Chrome and Microsoft Edge, untested in other browsers. The issue happens with and without 3D GPU acceleration.

Note: obviously, this is a Minimal, Complete, and Verifiable example and the real work is magnitudes more complex.

Hundrends of line shapes are drawn procedurally onto a canvas (<1ms per line) and then cached to img elements asynchronously using toDataUrl (~40ms per line) when idle, so that screen panning -- which is also a required feature -- works more smoothly, as moving an img element on the screen is much cheaper than moving a canvas element (or redrawing all lines on a single canvas), whether it's the element itself, the container, or the browser viewport that is translated into a given direction.

As such, generating mipmaps is not really an option, or only as a last resort, as it will come with a significant performance penalty (each mip-level would have to be cached onto a separate image, cutting performance in half at the very least). I'd like to believe it can be avoided, though. Redrawing line shapes on each zoom step will mercillesly obliterate performance down to a slideshow.

The following is the list of things I tried, no effect:

  • Hinting at better image quality by defining the following CSS property values, on the shapes elements itself, or the container:
image-rendering: pixelated | optimizeSpeed | optimizeQuality
  • Forcing elevating the rendering layer into GPU acceleration with a dummy transform
  • Using scale3d instead of scale
  • Halving img.width and img.height and then compensating by doubling img.style.width and img.style.height
  • Not caching canvas results into img and instead displaying the canvas element directly (slower performance, same bad quality)

I also tried using filter: blur when zoomed out, but it did not yield a better quality, as the blur effect itself is applied after the given shape has been rendered on screen.

What else can I do to get rid of this pixel-noise, besides creating downsampled versions of each shape, effectively creating a software-rendered mipmap (LOD) system? (which, as I wrote, would like to strongly avoid)


How is this question different from the array of similar questions investigating bad image quality from downscaling?

  • Here, it's the container that is downscaled (and with CSS transformations), instead of individual image elements. No manipulation is done to actual image data, which is what is discussed in similar topics.
like image 574
John Weisz Avatar asked Aug 03 '16 12:08

John Weisz


People also ask

How do I increase the sharpness of an image in CSS?

In particular, you need mix-blend-mode and CSS filters. Hover over the image to see the effect. Tweaking of parameters (and maybe using opacity to "fade" the effect, similar to the "intensity" setting of unsharp mask) can help in yielding more desireable results for particular use-cases.

How do I increase the resolution of an image in HTML?

One of the simplest ways to resize an image in the HTML is using the height and width attributes on the img tag. These values specify the height and width of the image element. The values are set in px i.e. CSS pixels. For example, the original image is 640×960.


2 Answers

You can get much better results using SVG images instead of PNG and they are also very easy to generate and embed in your code, you don't even need to host them.

As you can see in this demo the SVG won't pixelate or become blurry and you will get even better results in high resolution screens like the Retina ones.

The SVG code:

<svg class="shape" width="440px" height="319px" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
    <g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
        <path d="M1,316.040132 C1,316.040132 241.5837,339.680482 241.5837,163.497447 C241.5837,-12.6855883 439,2.31775322 439,2.31775322" id="Path" stroke="#50E3C2" stroke-width="2"></path>
    </g>
</svg>

Obviously is not perfect.

like image 143
David Gomez Avatar answered Oct 04 '22 18:10

David Gomez


The interpolation of an image with sharp edge will inevitably create artifacts. Even if it is antialised, even if it is blured. You can test this in Photoshop itself, you'll get the same result.

Even the mipmap approach won't work.

The inherent problem is that pixels aren't able to retain all the information of your complex vector shapes; and when you squeeze pixels, shapes get lost. There are two solutions to improve the quality:

  • either render the picture at a higher resolution before scaling it so there's more pixels to store the intent of your shape
  • or directly render your vector shapes at the scale you're aiming
like image 40
solendil Avatar answered Oct 04 '22 18:10

solendil