Tuesday, August 19, 2014

Drawing Line Using Javascript (with Anti-alias Smoothening)


Caveat: It will not look good if the line is steep. Extra work is required to fix this script to render good steep lines.

This is my in-house recipe for anti-alias smoothening of line drawing using Javascript. It is not perfect and it is mainly for educational purposes. It might not be suitable for commercial web sites. If you don't want the anti-alias feature, you can try my plain line drawing Javascript.

Here we go:


<html>

<script>

var spanTag,spanTag1,spanTag2;
var red,red1,red2;
var green,green1,green2;
var blue,blue1,blue2;
var colorChannel;

function mkDotAS(x,y,color,aliasingFactor) {

  colorChannel = color.match(/\#(\w\w)(\w\w)(\w\w)/);

  red = parseInt(colorChannel[1],16);
  green = parseInt(colorChannel[2],16);
  blue = parseInt(colorChannel[3],16);

  red1 = red + parseInt((255-red) * aliasingFactor);
  green1 = green + parseInt((255-green) * aliasingFactor);
  blue1 = blue + parseInt((255-blue) * aliasingFactor);
  red1 = red1>255?255:red1; green1 = green1>255?255:green1; blue1 = blue1>255?255:blue1;

  red2 = red + parseInt((255-red) * (1-aliasingFactor));
  green2 = green + parseInt((255-green) * (1-aliasingFactor));
  blue2 = blue + parseInt((255-blue) * (1-aliasingFactor));
  red2 = red2>255?255:red2; green2 = green2>255?255:green2; blue2 = blue2>255?255:blue2;

  spanTag = document.createElement("span");
  spanTag.innerHTML = '<img src="dummy1x1.png" height=1 width=1>';
  spanTag.style.position = "absolute";
  spanTag.style.zIndex = 1;
  spanTag.style.left = x + "px";
  spanTag.style.top = y + "px";

  spanTag1 = document.createElement("span");
  spanTag1.innerHTML = '<img src="dummy1x1.png" height=1 width=1>';
  spanTag1.style.position = "absolute";
  spanTag1.style.zIndex = 1;
  spanTag1.style.left = x + "px";
  y--;
  spanTag1.style.top = y + "px";

  spanTag2 = document.createElement("span");
  spanTag2.innerHTML = '<img src="dummy1x1.png" height=1 width=1>';
  spanTag2.style.position = "absolute";
  spanTag2.style.zIndex = 1;
  spanTag2.style.left = x + "px";
  y+=2;
  spanTag2.style.top = y + "px";

  red = red>=16?red.toString(16):"0"+red.toString(16);
  green = green>=16?green.toString(16):"0"+green.toString(16);
  blue = blue>=16?blue.toString(16):"0"+blue.toString(16);

  red1 = red1>=16?red1.toString(16):"0"+red1.toString(16);
  green1 = green1>=16?green1.toString(16):"0"+green1.toString(16);
  blue1 = blue1>=16?blue1.toString(16):"0"+blue1.toString(16);

  red2 = red2>=16?red2.toString(16):"0"+red2.toString(16);
  green2 = green2>=16?green2.toString(16):"0"+green2.toString(16);
  blue2 = blue2>=16?blue2.toString(16):"0"+blue2.toString(16);

  spanTag.style.backgroundColor = "#"+red+green+blue;
  spanTag1.style.backgroundColor = "#"+red1+green1+blue1;
  spanTag2.style.backgroundColor = "#"+red2+green2+blue2;

  document.body.appendChild(spanTag);
  document.body.appendChild(spanTag1);
  document.body.appendChild(spanTag2);
}

function drawLineAS(x1,y1,x2,y2,color) {

  var i,j,x,y,m,d,done=0;
  var intMx,floatMx,diff=0;
  x=Math.abs(x1);
  y=Math.abs(y1);
  m=(y2-y1)/(x2-x1);
  d=x1>x2?"-":"+";
  c=y1-(m*x);
 
  while (!done) {
   mkDotAS(x,y,color,diff);
   if (x==x2) { done = 1; }
   if (d=="-") { x--; }
   else { x++; }

   y = parseInt(m*x) + c;
   floatMx = m*x; intMx = parseInt(m*x); diff = floatMx-intMx;
  }
}

window.onload = function() { // only can call after the body is loaded
  drawLineAS(10,40,1000,320,"#3388cc");
};

</script>

<body>
...
</body>

</html>


Actually, this is quite similar to my non-anti-alias version. You can refer to my earlier post on the basic of line drawing using Javascript. To make the story short, a picture worths a thousand words:



Beside the original non-anti-alias line, I just add the spanTag1 and spanTag2 which respectively being placed 1 pixel above and below the original non-anti-alias line. spanTag1 and spanTag2 are the lighter version of the original pixel. The brightness depends on the difference between the floating number of m*x and the integer number of m*x. Since our screen cannot take floating number, the integer number or rounded number offers an inaccurate representation of the actual line. The difference (between floating and integer of m*x) is then being used to calculate the intensity of the lighter version of original pixel color. The one above and the below are being made to be complementing each other to make up a complete bright pixel. This means that if the one above is 25% brighter, the one below will be 75% brighter and vice versa. It is a bit hard to explain, but here it is. I hope this post is clear enough for those who want to know the basic of anti-aliasing for line drawing. Enjoy!

P.S.: In order for the above example to work, you need to place the dummy1x1.png in the same folder you running the HTML. You can get the dummy transparent 1x1 pixel image here if you don't image editing software to help you.

No comments:

Post a Comment