2016-09-24 45 views
0

我只是試圖將imagedata轉換爲高度圖,以便在畫布上顯示。但是當我這樣做時,對於我測試過的所有圖像都會出現一種奇怪的現象。試圖將imagedata轉換爲高度圖

這裏是我的代碼:

window.onload = function() 
 
{ 
 
\t var canvas = document.getElementById('game'); 
 
\t if(!canvas) 
 
\t { 
 
\t \t alert("Impossible de récupérer le canvas."); 
 
\t \t return; 
 
\t } 
 
\t 
 
\t var context = canvas.getContext('2d'); 
 
\t if(!context) 
 
\t { 
 
\t \t alert("Impossible de récupérer le contexte du canvas."); 
 
\t \t return; \t \t 
 
\t } 
 
\t 
 
\t var img = new Image(); 
 
\t img.src = "noise.png"; 
 
\t 
 
\t var size = 250000; 
 
\t var data = new Float32Array(size); 
 
\t var pxlData = new Array(size); 
 

 
\t for (var i = 0; i < size; i ++) { 
 
     data[i] = 0 
 
    } 
 
\t 
 
\t for (var i = 0; i < size; i++) 
 
\t { 
 
\t \t pxlData[i] = new Array(4); 
 
\t \t pxlData[i][0] = 0; 
 
\t \t pxlData[i][1] = 0; 
 
\t \t pxlData[i][2] = 0; 
 
\t } 
 
\t 
 
\t img.onload = function() 
 
\t { 
 
\t \t context.drawImage(img, 0, 0); 
 
\t \t 
 
\t \t var imgd = context.getImageData(0, 0, 500, 500); 
 
\t 
 
\t \t context.clearRect(0, 0, canvas.width, canvas.height); 
 
\t \t 
 
\t \t var pix = imgd.data; 
 

 
\t \t var j=0; 
 
\t \t var x=0; 
 
\t \t var y=0; 
 
\t \t var i=0; 
 
\t \t 
 
\t \t for (var i = 0, n = pix.length; i < n; i += (4)) { 
 
\t \t \t var all = pix[i]+pix[i+1]+pix[i+2]; 
 
\t \t \t 
 
\t \t \t pxlData[j][0] = pix[i]; 
 
\t \t \t pxlData[j][1] = pix[i+1]; 
 
\t \t \t pxlData[j][2] = pix[i+2]; 
 
\t \t \t pxlData[j][3] = pix[i+3]; 
 
\t \t \t 
 
\t \t \t data[j++] = all/3; 
 
\t \t } \t \t 
 
\t \t 
 
\t \t var alpha; 
 
\t \t 
 
\t \t for(y = 0; y < 500; y++) 
 
\t \t { 
 
\t \t \t for(x = 0; x < 500; x++) 
 
\t \t \t { 
 
\t \t \t \t if(data[x * y] <= 100){ 
 
\t \t \t \t \t context.fillStyle = "blue"; 
 
\t \t \t \t }else if(data[x * y] >= 100){ 
 
\t \t \t \t \t context.fillStyle = "green"; 
 
\t \t \t \t } 
 
\t \t \t \t //context.fillStyle = 'rgba('+ data[x * y] +', '+ data[x * y] +', '+ data[x * y] +', 1)'; 
 
\t \t \t \t context.fillRect(x, y, 1, 1); 
 
\t \t \t \t 
 
\t \t \t \t // context.fillStyle = 'rgba('+ pxlData[x * y][0] +', '+ pxlData[x * y][1] +', '+ pxlData[x * y][2] +', '+ pxlData[x * y][3] +')'; 
 
\t \t \t \t // context.fillRect(x, y, 1, 1); 
 
\t \t \t } 
 
\t \t } 
 
\t }; 
 
\t 
 

 
}
<!DOCTYPE html> 
 
\t <html> 
 
\t \t <head> 
 
\t \t \t <link rel="stylesheet" href="style.css" type="text/css"> 
 
\t \t \t <script type="text/javascript" src="game.js"></script> 
 
\t \t \t <title>Génération de terrain</title> 
 
\t \t </head> 
 
\t \t <body> 
 
\t \t \t <canvas id="game" width="500" height ="500">Votre navigateur ne supporte pas les canvas.</canvas> 
 
\t \t </body> 
 
\t </html>

那就是它的期待,當我運行它,如:

canvas

+0

您試圖獲得什麼效果? – JonSG

回答

0

錯誤是如何索引的像素32位浮點數組。

你有data[x * y]這意味着在0,0像素將在索引0 * 0 = 0和像素在0,100也將在0 * 100 = 0和所有其他索引將是錯誤的。爲了獲得正確的像素地址,使用x + y * width當從一個項目是像素的數組進行索引時使用x + y * width。如果索引爲像素數據「imageData.data」每個像素都是4項(R,G,B,A),因此會使用data[x * 4 + y * canvas.width * 4]或者更簡單地說imageData.data[x + y * canvas.width * 4]

你的代碼看你有創造一些常見的錯誤,這將使你的代碼運行速度比它能做的要慢。我簡化了你的代碼。它做同樣的事情,但沒有所有的開銷。我添加了評論,刪除了您的代碼,並提出了相同的替代方法。

最大的變化是渲染綠色和藍色循環。你在哪裏設置每個像素context.fillRect(x,y,1,1);這是非常非常緩慢的。不是爲每個像素繪製一個矩形,而是使用您獲得的imageData並在讀取高度後填充顏色,然後將該數據重新放回畫布上。我使用了兩個typeArray視圖來設置和讀取數據,這也提高了性能。

// convert r,g,b,a to 32 bit colour using correct little or big endian 
function create32Pixel(r, g, b, a){ // dont call this inside loops as very slow 
    var endianConvert = new Uint8ClampedArray(4); // use to convert to correct endian 
    var endianConvert32 = new Uint32Array(endianConvert.buffer); 
    endianConvert[0] = r; 
    endianConvert[1] = g; 
    endianConvert[2] = b; 
    endianConvert[3] = a; 
    return endianConvert32[0]; 
} 

window.onload = function() 
{ 
    var canvas = document.getElementById('game'); 
    if(!canvas) 
    { 
     alert("Impossible de récupérer le canvas."); 
     return; 
    } 

    var context = canvas.getContext('2d'); 
    if(!context) 
    { 
     alert("Impossible de récupérer le contexte du canvas."); 
     return;  
    } 

    var img = new Image(); 
    img.src = "noise.png"; 

    var size = 250000; 
    // Do you really need floats?? 16 bit unsigned int array can hold 255 * 3 and all javascript 
    // numbers are converted to 64 bit floats so you will not lose precision from original when manipulating the 16bit values. 
    // following array is not needed. 
    //var dataFloat = new Float32Array(size); 
    // following array is not needed. 
    //var pxlData = new Array(size); // bad way to create an array 
    //var pxlData = []; // create empty array and push onto it. 


    // can use dataFloat.fill() 
    /*for (var i = 0; i < size; i ++) { 
     dataFloat[i] = 0 
    }*/ 
    //dataFloat.fill(0); // but not needed as array is zeroed when created (not from an existing buffer) 

    // Very inefficient as you are creating a new array for every pixel. Use flat array instead. 
    /*for (var i = 0; i < size; i++) 
    { 
     pxlData[i] = new Array(4); 
     pxlData[i][0] = 0; 
     pxlData[i][1] = 0; 
     pxlData[i][2] = 0; 
    }*/ 
    // should do 
    /*var i; 
    while(i < size * 4){ 
     pxlData[i++] = 0; // array grows as you increase i; 
    }*/ 

    img.onload = function() 
    { 
     context.drawImage(img, 0, 0); 

     var imgd = context.getImageData(0, 0, canvas.width, canvas.height); 

     // don't need to clear 
     // context.clearRect(0, 0, canvas.width, canvas.height); 

     // make two views one 8bit and the other 32bit. Both point to the same data change one 
     // changes the other 
     var pixChannels = imgd.data; 
     var pixels = new Uint32Array(pixChannels.buffer); 

     var j,x,y,j; 
     j = x = y = i = 0; 


     // Create pixel colours. Need to ensure correct order as some systems 
     // use little edian and others big endian 
     // see https://en.wikipedia.org/wiki/Endianness for info. 
     var green = create32Pixel(0,255,0,255); 
     var blue = create32Pixel(0,0,255,255); 


     // use j as 32bit pixel index and i as 8bit index 
     // read the height and set pixel colour accordingly. 
     while(j < pixels.length){ 
      var height = pixChannels[i++] + pixChannels[i++] + pixChannels[i++]; 
      if(height <= 300){ // no need to divide by 3 just test for 3 time 100 
       pixels[j++] = blue; 
      }else{ 
       pixels[j++] = green; 
      } 
      i++; // skip alpha channel 
     } 
     context.putImageData(imgd,0,0); // put pixels back to canvas. 

    }; 



}