2016-06-07 70 views
4

我正在創建繪圖應用程序。我已經成功地完成了一切。當我用深色繪製圖像時,邊緣會出現一些白色像素。我試圖改變r + g + b的值,也是alpha,但沒用。誰能幫我嗎?您可以從here查看實時網站。任何人都會幫助我,我會給予他50元的賞金。謝謝。這是我的代碼。畫布 - 填充在邊緣留下白色像素

<script type="text/javascript"> 
    var initial = screen.width - 50; 

    if (initial < 1000) { 
     initial = 1000; 
    } 

    var firsttime = null; 

    var colorYellow = { 
     r: 255, 
     g: 207, 
     b: 51 
    }; 

    var context; 
    var canvasWidth = initial; 
    var canvasHeight = initial; 
    var myColor = colorYellow; 
    var curColor = myColor; 
    var outlineImage = new Image(); 
    var backgroundImage = new Image(); 
    var drawingAreaX = 0; 
    var drawingAreaY = 0; 
    var drawingAreaWidth = initial; 
    var drawingAreaHeight = initial; 
    var colorLayerData; 
    var outlineLayerData; 
    var totalLoadResources = 2; 
    var curLoadResNum = 0; 
    var undoarr = new Array(); 
    var redoarr = new Array(); 

    function history(command) { // handles undo/redo button events. 
     var data; 
     if (command === "redo") { 
      data = historyManager.redo(); // get data for redo 
     } else 
     if (command === "undo") { 
      data = historyManager.undo(); // get data for undo 
     } 
     if (data !== undefined) { // if data has been found 
      setColorLayer(data); // set the data 
     } 
    } 

    // sets colour layer and creates copy into colorLayerData 
    function setColorLayer(data) { 
     context.putImageData(data, 0, 0); 
     colorLayerData = context.getImageData(0, 0, canvasWidth, canvasHeight); 
     context.drawImage(backgroundImage, 0, 0, canvasWidth, canvasHeight); 
     context.drawImage(outlineImage, 0, 0, drawingAreaWidth, drawingAreaHeight); 
    } 

    // Clears the canvas. 
    function clearCanvas() { 
     context.clearRect(0, 0, context.canvas.width, context.canvas.height); 
    } 



    // Draw the elements on the canvas 
    function redraw() { 
     uc = 0; 
     rc = 0; 
     var locX, 
      locY; 

     // Make sure required resources are loaded before redrawing 
     if (curLoadResNum < totalLoadResources) { 
      return; // To check if images are loaded successfully or not. 
     } 

     clearCanvas(); 
     // Draw the current state of the color layer to the canvas 
     context.putImageData(colorLayerData, 0, 0); 

     historyManager.push(context.getImageData(0, 0, canvasWidth, canvasHeight)); 
     redoarr = new Array(); 
     // Draw the background 
     context.drawImage(backgroundImage, 0, 0, canvasWidth, canvasHeight); 

     // Draw the outline image on top of everything. We could move this to a separate 
     // canvas so we did not have to redraw this everyime. 
     context.drawImage(outlineImage, 0, 0, drawingAreaWidth, drawingAreaHeight); 
    }; 

    function matchOutlineColor(r, g, b, a) { 

     return (r + g + b < 50 && a >= 50); 
    }; 

    function matchStartColor(pixelPos, startR, startG, startB) { 

     var r = outlineLayerData.data[pixelPos], 
      g = outlineLayerData.data[pixelPos + 1], 
      b = outlineLayerData.data[pixelPos + 2], 
      a = outlineLayerData.data[pixelPos + 3]; 

     // If current pixel of the outline image is black 
     if (matchOutlineColor(r, g, b, a)) { 
      return false; 
     } 

     r = colorLayerData.data[pixelPos]; 
     g = colorLayerData.data[pixelPos + 1]; 
     b = colorLayerData.data[pixelPos + 2]; 

     // If the current pixel matches the clicked color 
     if (r === startR && g === startG && b === startB) { 
      return true; 
     } 

     // If current pixel matches the new color 
     if (r === curColor.r && g === curColor.g && b === curColor.b) { 
      return false; 
     } 

     return true; 
    }; 

    function colorPixel(pixelPos, r, g, b, a) { 
     colorLayerData.data[pixelPos] = r; 
     colorLayerData.data[pixelPos + 1] = g; 
     colorLayerData.data[pixelPos + 2] = b; 
     colorLayerData.data[pixelPos + 3] = a !== undefined ? a : 255; 
    }; 

    function floodFill(startX, startY, startR, startG, startB) { 

     document.getElementById('color-lib-1').style.display = "none"; 
     document.getElementById('color-lib-2').style.display = "none"; 
     document.getElementById('color-lib-3').style.display = "none"; 
     document.getElementById('color-lib-4').style.display = "none"; 
     document.getElementById('color-lib-5').style.display = "none"; 

     change = false; 

     var newPos, 
      x, 
      y, 
      pixelPos, 
      reachLeft, 
      reachRight, 
      drawingBoundLeft = drawingAreaX, 
      drawingBoundTop = drawingAreaY, 
      drawingBoundRight = drawingAreaX + drawingAreaWidth - 1, 
      drawingBoundBottom = drawingAreaY + drawingAreaHeight - 1, 
      pixelStack = [ 
       [startX, startY] 
      ]; 

     while (pixelStack.length) { 

      newPos = pixelStack.pop(); 
      x = newPos[0]; 
      y = newPos[1]; 

      // Get current pixel position 
      pixelPos = (y * canvasWidth + x) * 4; 

      // Go up as long as the color matches and are inside the canvas 
      while (y >= drawingBoundTop && matchStartColor(pixelPos, startR, startG, startB)) { 
       y -= 1; 
       pixelPos -= canvasWidth * 4; 
      } 

      pixelPos += canvasWidth * 4; 
      y += 1; 
      reachLeft = false; 
      reachRight = false; 

      // Go down as long as the color matches and in inside the canvas 
      while (y <= drawingBoundBottom && matchStartColor(pixelPos, startR, startG, startB)) { 
       y += 1; 

       colorPixel(pixelPos, curColor.r, curColor.g, curColor.b); 

       if (x > drawingBoundLeft) { 
        if (matchStartColor(pixelPos - 4, startR, startG, startB)) { 
         if (!reachLeft) { 
          // Add pixel to stack 
          pixelStack.push([x - 1, y]); 
          reachLeft = true; 
         } 

        } else if (reachLeft) { 
         reachLeft = false; 
        } 
       } 

       if (x < drawingBoundRight) { 
        if (matchStartColor(pixelPos + 4, startR, startG, startB)) { 
         if (!reachRight) { 
          // Add pixel to stack 
          pixelStack.push([x + 1, y]); 
          reachRight = true; 
         } 
        } else if (reachRight) { 
         reachRight = false; 
        } 
       } 

       pixelPos += canvasWidth * 4; 
      } 
     } 
    }; 

    // Start painting with paint bucket tool starting from pixel specified by startX and startY 
    function paintAt(startX, startY) { 

     var pixelPos = (startY * canvasWidth + startX) * 4, 
      r = colorLayerData.data[pixelPos], 
      g = colorLayerData.data[pixelPos + 1], 
      b = colorLayerData.data[pixelPos + 2], 
      a = colorLayerData.data[pixelPos + 3]; 

     if (r === curColor.r && g === curColor.g && b === curColor.b) { 
      // Return because trying to fill with the same color 
      return; 
     } 

     if (matchOutlineColor(r, g, b, a)) { 
      // Return because clicked outline 
      return; 
     } 

     floodFill(startX, startY, r, g, b); 

     redraw(); 
    }; 

    // Add mouse event listeners to the canvas 
    function createMouseEvents() { 

     $('#canvas').mousedown(function(e) { 

      // Mouse down location 
      var mouseX = e.pageX - this.offsetLeft, 
       mouseY = e.pageY - this.offsetTop; 

      // assuming that the mouseX and mouseY are the mouse coords. 
      if (this.style.width) { // make sure there is a width in the style 
       // (assumes if width is there then height will be too 
       var w = Number(this.style.width.replace("px", "")); // warning this will not work if size is not in pixels 
       var h = Number(this.style.height.replace("px", "")); // convert the height to a number 
       var pixelW = this.width; // get the canvas resolution 
       var pixelH = this.height; 
       mouseX = Math.floor((mouseX/w) * pixelW); // convert the mouse coords to pixel coords 
       mouseY = Math.floor((mouseY/h) * pixelH); 
      } 

      if ((mouseY > drawingAreaY && mouseY < drawingAreaY + drawingAreaHeight) && (mouseX <= drawingAreaX + drawingAreaWidth)) { 
       paintAt(mouseX, mouseY); 
      } 
     }); 
    }; 

    resourceLoaded = function() { 

     curLoadResNum += 1; 
     //if (curLoadResNum === totalLoadResources) { 
     createMouseEvents(); 
     redraw(); 
     //} 
    }; 

    var historyManager = (function() { // Anon for private (closure) scope 
     var uBuffer = []; // this is undo buff 
     var rBuffer = []; // this is redo buff 
     var currentState = undefined; // this holds the current history state 
     var undoElement = undefined; 
     var redoElement = undefined; 
     var manager = { 
      UI: { // UI interface just for disable and enabling redo undo buttons 
       assignUndoButton: function(element) { 
        undoElement = element; 
        this.update(); 
       }, 
       assignRedoButton: function(element) { 
        redoElement = element; 
        this.update(); 
       }, 
       update: function() { 
        if (redoElement !== undefined) { 
         redoElement.disabled = (rBuffer.length === 0); 
        } 
        if (undoElement !== undefined) { 
         undoElement.disabled = (uBuffer.length === 0); 
        } 
       } 
      }, 
      reset: function() { 
       uBuffer.length = 0; 
       rBuffer.length = 0; 
       currentState = undefined; 
       this.UI.update(); 
      }, 
      push: function(data) { 
       if (currentState !== undefined) { 
        var same = true 
        for (i = 0; i < data.data.length; i++) { 
         if (data.data[i] !== currentState.data[i]) { 
          same = false; 
          break; 
         } 
        } 
        if (same) { 
         return; 
        } 
       } 
       if (currentState !== undefined) { 
        uBuffer.push(currentState); 
       } 
       currentState = data; 
       rBuffer.length = 0; 
       this.UI.update(); 
      }, 
      undo: function() { 
       if (uBuffer.length > 0) { 
        if (currentState !== undefined) { 
         rBuffer.push(currentState); 
        } 
        currentState = uBuffer.pop(); 
       } 
       this.UI.update(); 
       return currentState; // return data or unfefined 
      }, 
      redo: function() { 
       if (rBuffer.length > 0) { 
        if (currentState !== undefined) { 
         uBuffer.push(currentState); 
        } 
        currentState = rBuffer.pop(); 
       } 
       this.UI.update(); 
       return currentState; 
      }, 
     } 
     return manager; 
    })(); 

    function start() { 

     var canvas = document.createElement('canvas'); 
     canvas.setAttribute('width', canvasWidth); 
     canvas.setAttribute('height', canvasHeight); 
     canvas.setAttribute('id', 'canvas'); 
     document.getElementById('canvasDiv').appendChild(canvas); 

     if (typeof G_vmlCanvasManager !== "undefined") { 
      canvas = G_vmlCanvasManager.initElement(canvas); 
     } 
     context = canvas.getContext("2d"); 
     backgroundImage.onload = resourceLoaded(); 
     backgroundImage.src = "images/t.png"; 

     outlineImage.onload = function() { 
      context.drawImage(outlineImage, drawingAreaX, drawingAreaY, drawingAreaWidth, drawingAreaHeight); 

      try { 
       outlineLayerData = context.getImageData(0, 0, canvasWidth, canvasHeight); 
      } catch (ex) { 
       window.alert("Application cannot be run locally. Please run on a server."); 
       return; 
      } 
      clearCanvas(); 
      colorLayerData = context.getImageData(0, 0, canvasWidth, canvasHeight); 
      resourceLoaded(); 
     }; 
     outlineImage.src = "images/products/<?php echo $product['product_image']; ?>"; 
    }; 

    if (historyManager !== undefined) { 
     // only for visual feedback and not required for the history manager to function. 
     historyManager.UI.assignUndoButton(document.querySelector("#undo-button")); 
     historyManager.UI.assignRedoButton(document.querySelector("#redo-button")); 
    } 

    getColor = function() { 

    }; 
</script> 
+1

這有什麼** **做jQuery的。 –

回答

2

我建議兩個更改:

  1. 混合像素&填充顏色而不是硬覆蓋
  2. 基於強度梯度的變化,而不是簡單的閾值

灌裝在水平和垂直限制填充區域直到強度梯度的符號從+到 - 或 - 到+使我們填充整個區域(包括黑色邊框的「一半」)。通過檢查梯度,我們確保不超過強度最小值,從而避免填充鄰近區域。

看一看下面的演示:

// Get pixel intensity: 
 
function getIntensity(data, i) { 
 
    return data[i] + data[i + 1] + data[i + 2]; 
 
} 
 

 
// Set pixel color: 
 
function setColor(data, i, r, g, b) { 
 
    data[i] &= r; 
 
    data[i + 1] &= g; 
 
    data[i + 2] &= b; 
 
} 
 

 
// Fill a horizontal line: 
 
function fill(x, y, data, width, r, g, b) { 
 
    var i_start = y * (width << 2); 
 
    var i = i_start + (x << 2); 
 
    var i_end = i_start + (width << 2); 
 
    var i_intensity = getIntensity(data, i); 
 

 
    // Horizontal line to the right: 
 
    var prev_gradient = 0; 
 
    var prev_intensity = i_intensity; 
 
    for (var j = i; j < i_end; j += 4) { 
 
    var intensity = getIntensity(data, j); 
 
    gradient = intensity - prev_intensity; 
 
    prev_intensity = intensity; 
 
    if ((prev_gradient > 0 && gradient < 0) || (prev_gradient < 0 && gradient > 0)) break; 
 
    if (gradient != 0) prev_gradient = gradient; 
 

 
    setColor(data, j, 255, 0, 0); 
 
    } 
 

 
    // Horizontal line to the left: 
 
    prev_gradient = 0; 
 
    prev_intensity = i_intensity; 
 
    for (var j = i - 4; j > i_start; j -= 4) { 
 
    var intensity = getIntensity(data, j); 
 
    gradient = intensity - prev_intensity; 
 
    prev_intensity = intensity; 
 
    if ((prev_gradient > 0 && gradient < 0) || (prev_gradient < 0 && gradient > 0)) break; 
 
    if (gradient != 0) prev_gradient = gradient; 
 

 
    setColor(data, j, 255, 0, 0); 
 
    } 
 
} 
 

 
// Demo canvas: 
 
var canvas = document.getElementById('canvas'); 
 
var context = canvas.getContext('2d'); 
 

 
// Fill horizontal line on click: 
 
canvas.addEventListener('mousedown', event => { 
 
    var rect = canvas.getBoundingClientRect(); 
 
    var x = event.clientX - rect.left | 0; 
 
    var y = event.clientY - rect.top | 0; 
 

 
    var imageData = context.getImageData(0, 0, canvas.width, canvas.height); 
 
    fill(x, y, imageData.data, imageData.width); 
 
    context.putImageData(imageData, 0, 0); 
 
}); 
 

 
// Load a sample image: 
 
var image = new Image(); 
 
image.addEventListener('load', event => { 
 
    context.drawImage(event.target, 0, 0, canvas.width, canvas.height); 
 
}); 
 
image.src = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAsAAAACCAIAAAABwbG5AAAAA3NCSVQICAjb4U/gAAAAGXRFWHRTb2Z0d2FyZQBnbm9tZS1zY3JlZW5zaG907wO/PgAAACZJREFUCJlj+I8Kbt68mZeXd/PmTbgIw38MsG/fvoKCgqdPn0K4ACAfPGrloJp1AAAAAElFTkSuQmCC';
<canvas id="canvas"></canvas>

您可以通過跟蹤只有一個顏色通道,而不是總結三個爲您getIntensity功能提高性能。

另外,由於混合模式的原因,在用另一種顏色填充顏色之前,您需要先處理已經填充的區域。

你可以e。 G。將背景圖像的單通道灰度圖像數據陣列保存在內存中,並將其用作填充算法的源圖像。

使用二進制邊界將所有灰度圖像轉換爲1位輪廓,並將它們用作填充算法的來源,將結果與灰度圖像平滑混合,可能會更有效率(手動或自動)。 。

+0

讓我檢查它是否有效。 –

+0

你能改變我的代碼嗎? –

+0

我花了很多時間寫這個答案;編寫你的代碼太多了。但是,如果您有關於如何繼續的具體問題,請繼續詢問。 –

2

是的,由於白色和黑色之間存在微小的漸變,所以「白色」斑點實際上並不是白色。嘗試給它一些在這些線路上的餘地:

if ((r <= curColor.r + 10 && r >= curColor.r - 10) && (r >= curColor.g - 10 && r <= curColor.g + 10) && (b >= curColor.b - 10 && b <= curColor.b + 10)) { 
     return false; 
    } 

您可以修改10因素,直到它看起來不錯。只是調整它,直到它沒關係。 (可能是壞的代碼,我剛醒來,但你應該得到的圖片:D)

你也可以預處理圖像在一個單獨的緩衝區,並減少顏色的數量。這樣,填充漸變的開始就更容易了,從而減少或消除了你所描述的不希望的效果。

+0

我在哪裏改變這條線? –

+0

根據「//如果當前像素匹配點擊的顏色」。應該稍微好一些。 – overburn

+0

我做到了,但它是一樣的。沒有效果:(仍然出現白色點 –

2

說實話,這不是真的你的繪圖程序的錯誤,因爲正在繪製圖像的錯誤。 「白色」像素實際上是淺灰色,這是畫筆工具用於在圖像中繪製線條的副作用。有兩種方法可以解決這個問題:

  1. 刪除圖像中的所有淺灰色像素,並使它們變白。使用顏色選擇工具和鉛筆工具可以解決這個問題。唯一的副作用是某些點的線條可能看起來有點生澀。

  2. 當談到什麼顏色被塗抹時,給予一些寬大。所以,與其替換純白色,不如替換蒼白的灰色。任何直到#CCC(或rgb(204,204,204))的顏色都應該塗上顏色。

的代碼方案(2)如下:

if(r >= 204 && g >= 204 && b >= 204 && r === g && g === b){ 
    return true; 
} 

這簡單地檢查如果該像素是一個光灰度顏色並且如果是這樣,則返回true。使用此代替輪廓顏色檢查功能。

0

您的輪廓檢查簡直是太嚴格,標誌着淺灰色的像素爲那些你不色的,我只是調整了您的threshholds:

function matchOutlineColor (r,g,b,a,e) { 
    return a >= 150; 
} 

通知我高得多的數字爲白色值和阿爾法。你可以稍微調整一下,但這對我來說很好。下面是一些前和後的照片:

之前(在200%縮放) enter image description here

後(在200%縮放) enter image description here