日期:2014-05-17  浏览次数:20818 次

HTML5 Canvas 如何取消反锯齿绘图
HTML5 Canvas 如何取消反锯齿绘图(HTML5 Canvas how to turn off anti-aliasing drawing)一、问题的提出
我们都知道反锯齿(anti-aliasing)绘图给我们带来更好的视觉体验,有了这个技术,绘制的图形的边缘再不是以前毛毛躁躁的样子了。这就是采用反锯齿算法的功劳。其实质就是把要绘制的颜色边缘和背景颜色做适当的融合,在人的眼睛看来,有种像雾像雨又像风的感觉。HTML5 Canvas的绘图就是默认anti-aliasing的。其实作为一般的开发者,可以不关心这个东西的存在,好像anti-aliasing是理应如此的。但是,如果我们的用户非要看到non-anti-aliasing的效果呢?
这个类似有了酱油还要吃盐的问题。酱油是好,但是有人就是要吃盐,怎么办?
另外的需求是,即使有了酱油,我还是需要吃盐。为啥,酱油有它的好处,盐有盐的用处。
比如:当我们在Canvas上移动鼠标的时候,我需要知道我的鼠标位置在什么图形上,即著名的点选问题。Canvas以前的绘图软件解决这个问题有标准的方法,就是用图形的ID作为图形的颜色值,绘制在内存当中的后台画布上,当我们移动鼠标在前台显示的画布上,我们可以通过获取后台画布的该点的像素值(ID)来获得图形ID。
这样一切都近乎完美。后台画布与前台画布采用完全一样的绘图机制,不同点是前台画布采用用户看到的实际像素颜色值,而后台画布采用图形的ID作为图形绘制的像素颜色值。这里的前提是,我们能控制这些像素值,以确保它在被绘制到后台画布的时候不被改变,就是我让你画一个像素颜色=1,你别自作聪明搞出个=1.5。遗憾的是目前版本的HTML5 Canvas就是这种自作聪明的家伙。迄今为止,我们没办法控制去掉anti-aliasing这个自作聪明的算法。我试过即使把context.mozImageSmoothingEnabled=false也不行。
如果这儿谁有一句话的方法可以满足我上面讲的需求,那么这篇文章直接就等于是狗屎。我费了很多的努力研究出了这篇狗屎文,在这里以飨读者,包括我自己。
二、解决方法探讨
如何取消(废止)HTML5 Canvas的反锯齿功能,在http://stackoverflow.com上也有很多讨论。如果让HTML5实现者来解决这个问题,几乎就是一句话搞定的事情。然而需要我们一周的时间想各种点子。在HTML5 Canvas本身没解决这个问题之前,如果让有的人来解决,几乎要花掉几个月的时间,好在如果你看到这篇文章,你就可以告诉你的老板,你只需要几个晚上就可以解决了。下面我教你具体办法。
前台画布我不管它,你怎么画是你自己的问题,后台画布和前台一样大小,涂满黑色(#000000)。然后你在前台Canvas上画图形id=1的时候,同时在后台画布上用1为颜色画这个同样的图形。对了,我还没告诉你如何去掉anti-aliasing,如果不去掉anti-aliasing,系统可能给画出来的像素颜色是1.5,这显然不是你想要的,也不是我写这篇狗屎文的目的。
我只好用代码来说明问题。记住这是HTML5的代码,javascript而已。
在HTML5页面中有Canvas:
          This browser does not support HTML5 Canvas.   

创建前台画布:

var canvas = document.getElementById("_canvasView");
var context = canvas.getContext("2d");
创建后台画布:
  var backend = document.createElement("canvas");
  backend.width = canvas.width;
  backend.height = canvas.height;
  var backendContext = backend.getContext("2d");
// 后台画布涂上黑色(我相信anti-aliasing不会把它搞成灰色)

  backendContext.fillStyle = "#000000";
  backendContext.fillRect(0, 0, backend.width, backend.height);

// 获取后台画布对应的像素数组

  var imageObj = backendContext.getImageData( 0, 0, canvas.width, canvas.height);
  var imageData = imageObj.data;

如果你想给这个数组里的像素赋予颜色值,可以像下面的代码:
  function setPixel(imageData, width, height, x, y, red, green, blue)
  {
    // i 没做边界条件检查,留给读者自己完成。

    var i = (y*width+x)*4;
    imageData = red;
    imageData[i+1] = green;
    imageData[i+2] = blue;
    // 下面这个是Alpha,我们不用
    //DEL: imageData[i+3]=???;

  }

HTML5声称imageData是可以直接操控的。因此,这是我们良好的机会去直接设置像素值。把一个像素数组设置回绘图上下文和我写累了上厕所拉泡屎一样简单:
  backendContext.putImageData(imageData, 0, 0);
接下来是发挥你大学学习计算机图形学的智慧的时候了。

三、真的需要微积分么?微积分大家原理都知道,真正用的时候就发抖了。毕竟我们是写网页脚本的,研究基础代数可不是强项啊。微积分肯定是不需要的,因此计算机图形学也是不需要的。然而,的确有一种解决的办法,就是利用计算机图形学的知识。很底层啊,读者要有心理准备,别雷倒了。

view plainprint?

    function setPixel(imageData, W, H, x, y, r, g, b)  
    {  
      var i = (y*W+x)*4;  
      imageData = r;  
      imageData[i+1] = g;  
      imageData[i+2] = b;  
    }  
      
    /**
    * Bresenham's draw line algorithm
    */  
    function Bresenham_drawLine(imageData, width, height, x1, y1, x2, y2, r, g, b)  
    {  
      var dx = Math.abs(x2 - x1),  
          dy = Math.abs(y2 - y1),  
          yy = 0,  
          t;  
       
      if (dx < dy) {  
        yy = 1;  
        t=x1; x1=y1; y1=t;  
        t=x2; x2=y2; y2=t;  
        t=dx; dx=dy; dy=t;  
      }         
      
      var ix = (x2 - x1) > 0 ? 1 : -1,  
          iy = (y2 - y1) > 0 ? 1 : -1,  
          cx = x1,  
          cy = y1,  
          n2dy = dy * 2,  
          n2dydx = (dy - dx) * 2,  
          d = dy * 2 - dx;   
      
      if(yy==1) {     
        while(cx != x2) {  
          if(d < 0) {      
            d += n2dy;      
          }     
          else {      
            cy += iy;      
            d += n2dydx;      
          }  
      
          setPixel(imageData, width, height, cy, cx, r, g, b);  
          cx += ix;      
        }