歡迎您光臨本站 註冊首頁

HTML5 Canvas 如何取消反鋸齒繪圖

←手機掃碼閱讀     火星人 @ 2014-03-12 , reply:0
  

一、問題的提出

我們都知道反鋸齒(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:

   

創建前台畫布:

  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.widthbackend.height);

// 獲取後台畫布對應的像素數組

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

如果你想給這個數組裡的像素賦予顏色值,可以像下面的代碼:

  function setPixel(imageData, widthheight, x, y, red, green, blue)
  {

    // i 沒做邊界條件檢查,留給讀者自己完成。

    var i = (y*width+x)*4;
    imageData[i] = red;
    imageData[i+1] = green;
    imageData[i+2] = blue;

    // 下面這個是Alpha,我們不用

    //DEL: imageData[i+3]=???;

  }

HTML5聲稱imageData是可以直接操控的。因此,這是我們良好的機會去直接設置像素值。把一個像素數組設置回繪圖上下文和我寫累了上廁所拉泡屎一樣簡單:

  backendContext.putImageData(imageData, 0, 0);

接下來是發揮你大學學習計算機圖形學的智慧的時候了。

三、真的需要微積分么?

微積分大家原理都知道,真正用的時候就發抖了。畢竟我們是寫網頁腳本的,研究基礎代數可不是強項啊。微積分肯定是不需要的,因此計算機圖形學也是不需要的。然而,的確有一種解決的辦法,就是利用計算機圖形學的知識。很底層啊,讀者要有心理準備,別雷倒了。 
 
  1. function setPixel(imageData, W, H, x, y, r, g, b)  
  2. {  
  3.   var i = (y*W+x)*4;  
  4.   imageData[i] = r;  
  5.   imageData[i+1] = g;  
  6.   imageData[i+2] = b;  
  7. }  
  8.   
  9. /** 
  10.  * Bresenham's draw line algorithm 
  11.  */  
  12. function Bresenham_drawLine(imageData, width, height, x1, y1, x2, y2, r, g, b)  
  13. {  
  14.   var dx = Math.abs(x2 - x1),  
  15.       dy = Math.abs(y2 - y1),  
  16.       yy = 0,  
  17.       t;  
  18.    
  19.   if (dx < dy) {  
  20.     yy = 1;  
  21.     t=x1; x1=y1; y1=t;  
  22.     t=x2; x2=y2; y2=t;  
  23.     t=dx; dx=dy; dy=t;  
  24.   }          
  25.   
  26.   var ix = (x2 - x1) > 0 ? 1 : -1,  
  27.       iy = (y2 - y1) > 0 ? 1 : -1,  
  28.       cx = x1,  
  29.       cy = y1,  
  30.       n2dy = dy * 2,  
  31.       n2dydx = (dy - dx) * 2,  
  32.       d = dy * 2 - dx;   
  33.   
  34.   if(yy==1) {     
  35.     while(cx != x2) {  
  36.       if(d < 0) {      
  37.         d += n2dy;      
  38.       }     
  39.       else {      
  40.         cy += iy;      
  41.         d += n2dydx;      
  42.       }  
  43.   
  44.       setPixel(imageData, width, height, cy, cx, r, g, b);  
  45.       cx += ix;      
  46.     }  
  47.   }   
  48.   else {     
  49.     while(cx != x2) {      
  50.       if(d < 0) {      
  51.         d += n2dy;      
  52.       }  
  53.       else {      
  54.         cy += iy;      
  55.         d += n2dydx;      
  56.       }  
  57.    
  58.       setPixel(imageData, width, height, cx, cy, r, g, b);      
  59.       cx += ix;  
  60.     }  
  61.   }   
  62. }  
  63.   
  64. /** 
  65.  * Bresenham's draw circle algorithm 
  66.  */  
  67. function _draw_circle_8(imageData, W, H, xc, yc, x, y, r, g, b)  
  68. {      
  69.   setPixel(imageData, W, H, xc+x, yc+y, r, g, b);  
  70.   setPixel(imageData, W, H, xc-x, yc+y, r, g, b);  
  71.   setPixel(imageData, W, H, xc+x, yc-y, r, g, b);  
  72.   setPixel(imageData, W, H, xc-x, yc-y, r, g, b);  
  73.   setPixel(imageData, W, H, xc+y, yc+x, r, g, b);  
  74.   setPixel(imageData, W, H, xc-y, yc+x, r, g, b);  
  75.   setPixel(imageData, W, H, xc+y, yc-x, r, g, b);  
  76.   setPixel(imageData, W, H, xc-y, yc-x, r, g, b);  
  77. }  
  78.   
  79. function Bresenham_drawCircle(imageData, width, height, xc, yc, radius, fill, r, g, b)  
  80. {  
  81.   var x = 0,  
  82.       y = radius,  
  83.       yi,  
  84.       d = 3 - 2 * radius;  
  85.   
  86.   if (fill==1) {      
  87.     while (x <= y) {      
  88.       for (yi = x; yi <= y; yi ++) {    
  89.         _draw_circle_8(imageData, width, height, xc, yc, x, yi, r, g, b);      
  90.       }   
  91.       if (d < 0) {      
  92.         d = d + 4 * x + 6;      
  93.       }     
  94.       else {      
  95.         d = d + 4 * (x - y);      
  96.         y--;      
  97.       }  
  98.       x++;      
  99.     }      
  100.   }     
  101.   else {      
  102.     while (x <= y) {      
  103.       _draw_circle_8(imageData, width, height, xc, yc, x, y, r, g, b);   
  104.       if (d < 0) {      
  105.         d = d + 4 * x + 6;      
  106.       }     
  107.       else {      
  108.         d = d + 4 * (x - y);      
  109.         y--;      
  110.       }      
  111.       x++;  
  112.     }      
  113.   }      
  114. }  
有個叫Bresenham的傢伙,我們都要感謝他告訴我們怎麼把一條線畫到屏幕上。就是知道2個點坐標,從而知道如何點亮這條線經過的像素點的問題。我上面貼出來的代碼不是賣弄我的圖形學理論知識,實話說,代碼是google出來的,我只是去偽存真而已。但是按上面的方法的確可以實現我要的去掉反走樣,看看我工作的截圖:

 


上面這個圖就是帶有反走樣的前台畫布的現實效果。而下面的這個就是後台畫布去掉反走樣的現實效果。我就是採用上面的Bresenham演算法來實現的,運行效率還蠻高的。

其實用這種辦法,新帶來的問題比原來的更多更複雜:

1)都HTML5時代了,還要Bresenham這個老傢伙出來壯場子,當我白痴啊。

2)寫腳本的一般都是演算法小白,這樣會嚇傻人的。

3)HTML5 Canvas如果真是這樣用,豈不是更大的退步嗎?

4)最大的困難是那麼多圖形類型,每個都要用Bresenham演算法搞定,不是太獸獸了么?

好文,頂一下
(0)
0%
文章真差,踩一下
(1)
100%
------分隔線----------------------------
  • 上一篇:Linux下AT&T語法同Intel語法區別
  • 下一篇:軟體開發工具推薦 :Gow
  • 我要評論!
  • 收藏
  • 挑錯
  • 推薦
  • 列印


把開源帶在你的身邊-精美linux小紀念品
文章分類
    新聞動態 企鵝看世界 軟體更新資訊 新手入門 資料庫類 系統安全 系統管理 網路管理 使用經驗 編程開發 企業應用 硬體相關 Unix家族 觀點評論 人物介紹 技術前沿 專題 開源生活 開源美圖 英文資料 Eden團隊出品 開源軟體庫
軟體導航
  • 發行版類 內核相關 伺服器類 模擬模擬 文件管理
  • 系統安全 多媒體類 硬體工具 編程開發 網路熱門
  • 雜類工具 網路工具 圖形圖像 閱讀編輯 書籍資料
  • 遊戲軟體 辦公軟體 數據備份 中文相關 系統管理
  • 科學計算 資料庫類 XWin系統
論壇導航
  • 初級應用-> 新手入門 | 伺服器應用 | 中文化 | 軟體使用交流 | 硬體驅動 | 圖片秀 | 茶館
  • 高級應用->資料庫 | 系統安全 | 嵌入式應用|
  • 編程開發-> C/C++(STL/boost) | 內核 | RAD|Perl/PHP/Python | JAVA/XML | Shell
  • 發行版-> Redhat和Fedora | Debian | Gentoo | Slackware/Suse | Mandrake/Mandriva
  • Unix ->FreeBSD | Solaris | 其他Unix討論
論壇精華
一周熱點

[火星人 ] HTML5 Canvas 如何取消反鋸齒繪圖已經有1132次圍觀

http://coctec.com/docs/program/show-post-71418.html