1

Im looking for a way to kill banding on gradients in KineticJs. Looks horrible on low contrast black and white gradients. Thanks for any suggestion. enter image description here

koyotee
  • 45
  • 8

1 Answers1

2

You can diffuse gradient banding by adding Perlin Noise to the image

BTW, this is also one way you would do anti-banding in Photoshop.

After your canvas with gradient is complete, add a layer of Perlin Noise using a low alpha so it doesn’t overwelm the original image.

    // generate noise 
    var noise=perlinNoise();

    // reduce the alpha
    context.globalAlpha=.35;

    // draw the noise layer over the original canvas
    context.drawImage(noise,0,0,canvas.width,canvas.height);

This is what random Perlin noise layer looks like:

enter image description here

And this is the original image with the noise layer applied:

enter image description here

A few notes:

  • You can tweak both the context.globalAlpha and the alpha in RandomNoise(alpha) to your taste.
  • You can create and reuse 1 noise layer or create a new noise layer each time.
  • Adding noise will "soften" the original gradient.
  • Alternatively, you could apply a slight Kinetic blur filter.
  • If you need targeted noise, you can apply the noise using a clipping region (clipFunc).

Here’s code and a Fiddle: http://jsfiddle.net/m1erickson/p4Vaf/

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8" />
    <title>Noise</title>
    <style>
    #canvas{border:1px solid red;}
    body(background-color:blue;}
    </style>
</head>
<body>
    <canvas id="canvas" width=300 height=300></canvas><br>
    <canvas id="noisecanvas" width=300 height=300></canvas>
<script>

   var canvas = document.getElementById("canvas");
   var ctx = canvas.getContext('2d');
   var noiseCanvas=document.getElementById("noisecanvas");


    var img=new Image();
    img.onload=function(){
        canvas.width=img.width;
        canvas.height=img.height;
        noiseCanvas.width=img.width;
        noiseCanvas.height=img.height;
        ctx.drawImage(this,0,0,canvas.width,canvas.height);
        var noise=perlinNoise();
        ctx.globalAlpha=.35;
        ctx.drawImage(noise,0,0,canvas.width,canvas.height);
    }
    img.src="gradient.png";


    function randomNoise(alpha) {
        var x = 0;
        var y = 0;
        var width = canvas.width;
        var height = canvas.height;
        var g = noiseCanvas.getContext("2d");
        var imageData = g.getImageData(x, y, width, height);
        var random = Math.random;
        var pixels = imageData.data;
        var n = pixels.length;
        var i = 0;

        while (i < n) {
            pixels[i++] = pixels[i++] = pixels[i++] = (random() * 256) | 0;
            pixels[i++] = alpha;
        }
        g.putImageData(imageData, x, y);
        return noiseCanvas;
    }

    function perlinNoise() {
        var noise = randomNoise(45);
        var g = noiseCanvas.getContext("2d");
        g.save();

        /* Scale random iterations onto the canvas to generate Perlin noise. */
        for (var size = 4; size <= noise.width; size *= 2) {
            var x = (Math.random() * (noise.width - size)) | 0,
                y = (Math.random() * (noise.height - size)) | 0;
            g.globalAlpha = 4 / size;
            g.drawImage(noise, x, y, size, size, 0, 0, noiseCanvas.width, noiseCanvas.height);
        }

        g.restore();
        return noiseCanvas;
    }

</script>
</body>
</html>
markE
  • 102,905
  • 11
  • 164
  • 176
  • Great answer! but the noise seems to be too ruff for my goal. I'll give it a try anyway. I like the Idea of bluring it a little bit. You seems like a pro maybe you can help me with my rotation problem I need it really badly. Please take a look if you got the time: [link](http://stackoverflow.com/questions/17338153/kineticjs-rotate-to-mouseposition) or kineticjs rotate to mouseposition – koyotee Jun 30 '13 at 21:40