2016-06-07 2 views
4

Я создаю приложение для рисования. Мне удалось все сделать. Когда я рисую изображение темным цветом, на краях появляются белые пиксели. Я попытался изменить значение r + g + b, а также альфа, но не использовать. Может кто-нибудь мне помочь? Вы можете проверить сайт с here. Тот, кто поможет мне, я дам ему 50 бонусов. Благодарю. Это мой код.Canvas - заливка листьев белые пиксели по краям

<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. Ограничить область заливки на основе градиента интенсивности изменений вместо простого порога

Заполнение горизонтальной и вертикальной до тех пор, пока знак градиента интенсивности не перевернется с + на - OR - до +, чтобы мы заполнили всю область, включая «половину» черной границы. Проверяя градиент, мы просто следим за тем, чтобы не превышать минимум интенсивности и, таким образом, не заполнять соседнюю область.

Посмотрите на следующую демо:

// 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. г. сохранить одноканальный массив данных изображения в оттенках серого в фоновом изображении в памяти и использовать его в качестве исходного изображения для вашего алгоритма заполнения.

Может быть еще более эффективным (вручную или автоматически) преобразовать все ваши изображения в оттенках серого в 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)) должен быть окрашен.

Код для второго варианта заключается в следующем:

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

Это просто проверяет, является ли пиксель свет в оттенках серого цвета и возвращает истину, если это так. Используйте это вместо функции проверки цвета.

0

Вашего чек для набросков просто слишком строг, маркировка светло-серого цвета пикселей, как те, которые вы не можете покрасить в я просто подправил свои пороги:

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

Уведомления мои гораздо более высокие показатели для белого значения и альфа. Возможно, вы, возможно, измените это, но это выглядит хорошо для меня. Вот некоторые до и после фотографии:

До (увеличенный в 200%) enter image description here

После (увеличенный в 200%) enter image description here

Смежные вопросы