简体   繁体   中英

Canvas flood fill stroke color

I'm building a small application using canvas. My application will have an option to fill a black and white image.

I downloaded a code and is working fine, but It only works when the image stroke is black. All images that I am going to use have grey stroke.

So, I would like to know what do I need to change to put the code working with grey strokes, instead of black strokes.

Here the code:

https://jsfiddle.net/mx0fmdh3/

HTML:

<canvas id="canvas" width=250 height=243></canvas>

JavaScript

var canvas = document.getElementById("canvas");
var context = canvas.getContext("2d");
var $canvas = $("#canvas");
var canvasOffset = $canvas.offset();
var offsetX = canvasOffset.left;
var offsetY = canvasOffset.top;
var canvasWidth = canvas.width;
var canvasHeight = canvas.height;
var strokeColor = {
    r: 152,
    g: 152,
    b: 152
};
var fillColor = {
    r: 101,
    g: 155,
    b: 65
};
var fillData;
var strokeData;


// load image
var img = new Image();
img.onload = function () {
    start();
}
img.crossOrigin = "anonymous";
img.src = "http://i.imgur.com/kjY1kiE.png";


function matchstrokeColor(r, g, b, a) {
    // never recolor the initial black divider strokes
    // must check for near black because of anti-aliasing
    return (r + g + b < 100 && a === 155);
}

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

    // get the color to be matched
    var r = strokeData.data[pixelPos],
        g = strokeData.data[pixelPos + 1],
        b = strokeData.data[pixelPos + 2],
        a = strokeData.data[pixelPos + 3];

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

    // get the potential replacement color
    r = fillData.data[pixelPos];
    g = fillData.data[pixelPos + 1];
    b = fillData.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 === fillColor.r && g === fillColor.g && b === fillColor.b) {
        return false;
    }

    return true;
}

// Thank you William Malone!
function floodFill(startX, startY, startR, startG, startB) {
    var newPos;
    var x;
    var y;
    var pixelPos;
    var neighborLeft;
    var neighborRight;
    var 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 >= 0 && matchStartColor(pixelPos, startR, startG, startB)) {
            y -= 1;
            pixelPos -= canvasWidth * 4;
        }

        pixelPos += canvasWidth * 4;
        y += 1;
        neighborLeft = false;
        neighborRight = false;

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

            fillData.data[pixelPos] = fillColor.r;
            fillData.data[pixelPos + 1] = fillColor.g;
            fillData.data[pixelPos + 2] = fillColor.b;
            fillData.data[pixelPos + 3] = 255;


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

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

            pixelPos += canvasWidth * 4;
        }
    }
}

// Start a floodfill
// 1. Get the color under the mouseclick
// 2. Replace all of that color with the new color
// 3. But respect bounding areas! Replace only contiguous color.
function paintAt(startX, startY) {

    // get the clicked pixel's [r,g,b,a] color data
    var pixelPos = (startY * canvasWidth + startX) * 4,
        r = fillData.data[pixelPos],
        g = fillData.data[pixelPos + 1],
        b = fillData.data[pixelPos + 2],
        a = fillData.data[pixelPos + 3];

    // this pixel's already filled
    if (r === fillColor.r && g === fillColor.g && b === fillColor.b) {
        return;
    }

    // this pixel is part of the original black image--don't fill
    if (matchstrokeColor(r, g, b, a)) {
        return;
    }

    // execute the floodfill
    floodFill(startX, startY, r, g, b);

    // put the colorized data back on the canvas
    context.clearRect(0, 0, canvasWidth, canvasHeight);
    context.putImageData(fillData, 0, 0);
    context.drawImage(img, 0, 0);
}

// create a random color object {red,green,blue}
function randomColorRGB() {
    var hex = Math.floor(Math.random() * 16777215).toString(16);
    //var r = parseInt(hex.substring(0, 2), 16);

    var r = 155;
    var g = 155;
    var b = 255;

    //var g = parseInt(hex.substring(2, 4), 16);
    //var b = parseInt(hex.substring(4, 6), 16);
    return ({
        r: r,
        g: g,
        b: b
    });
}

// draw the image to the canvas and get its pixel array
// listen for mouse clicks and do floodfill when clicked
function start() {

    context.drawImage(img, 0, 0);
    strokeData = context.getImageData(0, 0, canvasWidth, canvasHeight);
    context.clearRect(0, 0, context.canvas.width, context.canvas.height);
    fillData = context.getImageData(0, 0, canvasWidth, canvasHeight);
    context.drawImage(img, 0, 0);

    $('#canvas').mousedown(function (e) {
        // Mouse down location
        var mouseX = parseInt(e.clientX - offsetX);
        var mouseY = parseInt(e.clientY - offsetY);
        // set a new random fillColor
        fillColor = randomColorRGB();
        // floodfill
        paintAt(mouseX, mouseY);
    });

Thank you.

The match stroke function:

function matchstrokeColor(r, g, b, a) {
    // never recolor the initial black divider strokes
    // must check for near black because of anti-aliasing
    return (r + g + b < 100 && a === 155);
}

is only check whether rgb is a small number and is a painted stroke, as you directly paint your image on that canvas, rgb should now become something else, and alpha is now 255 (or unpredictable if your image has alpha).

Try change it to something that is aware of the storke's color, like sqrt distance:

// A small threshold would make it fill closer to stroke.
var strokeThreshold = 1;
function matchstrokeColor(r, g, b, a) { 
    // Use sqrt difference to decide its storke or not.
    var diffr = r - strokeColor.r;
    var diffg = g - strokeColor.g;
    var diffb= b - strokeColor.b;
    var diff = Math.sqrt(diffr * diffr + diffg * diffg + diffb * diffb) / 3;
    return (diff < strokeThreshold);
}

See Example jsfiddle

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM