## Friday, May 6, 2011

Suppose we need to draw linear gradient, but in a way which lets us to control color distribution between gradient parts. How to do that ? Answer is - gradient transfer function.

Algorithm is this:
1. Extract pixel's relative distance [0..1] from the start of gradient.
2. Update this distance by feeding it to gradient transfer function.
3. Blend source color with target color using updated distance as blend ratio.
4. Set calculated color to pixel.

We will use such gradient transfer function:
where x is pixel's relative distance from the start and a,b are some adjustable parameters.

Below is Javascript implementation of this method (your browser must support HTML5 canvas element). You can try to change a,b parameters of transfer function and see what happens to gradient.

a 0.5

b 5.00

Sorry, canvas not supported!

And here is Javascript code which does that (plot is generated with FLOT library):

```function showValue(newValue,el)
{
document.getElementById(el).innerHTML=parseFloat(newValue).toFixed(2);
GeneratePlot();
}

function Clamp(x,a,b) {
return Math.min(Math.max(x, a), b);
};

function NonLinearTransfer(x,a,b) {
return (1-a)*x + a*Math.pow(1+Math.exp(b-2*b*x),-1);
};

function GeneratePlot() {
var data = [];
var a =  document.getElementById("rngNonlinearity").value;
var b =  document.getElementById("rngAbruptness").value;

for (var i = 0; i <= 1; i += 0.01)
data.push([i, Clamp(NonLinearTransfer(i,a,b),0,1)]);

\$.plot(\$("#placeholder"),
[{ data: data, label: "Transfer function"}],
{
xaxes: [ { min: 0, max: 1 }],
yaxes: [ { min: 0, max: 1 }],
legend: { position: 'nw' }
}
);
};

function Blend(k,x,y) {
return (1-k)*x + k*y;
}

function setPixel(imageData, x, y, r, g, b, a) {
index = (x + y * imageData.width) * 4;
imageData.data[index+0] = r;
imageData.data[index+1] = g;
imageData.data[index+2] = b;
imageData.data[index+3] = a;
}

c = element.getContext("2d");

width = parseInt(element.getAttribute("width"));
height = parseInt(element.getAttribute("height"));

imageData = c.createImageData(width, height);

scolor = [0,255,0];
tcolor = [0,0,255];
c1 =  document.getElementById("rngNonlinearity").value;
c2 =  document.getElementById("rngAbruptness").value;

for (x = 0; x < width; x++) {
k = x/width;
k = NonLinearTransfer(k,c1,c2);
r = Blend(k,scolor[0],tcolor[0]);
g = Blend(k,scolor[1],tcolor[1]);
b = Blend(k,scolor[2],tcolor[2]);
for (y = 0; y < height; y++) {
setPixel(imageData, x, y, r, g, b, 0xff);
}
}

c.putImageData(imageData, 0, 0);
}
```

Have fun!