我正在尝试填充由其他不同颜色的区域包围的随机区域。 我目前正在使用4-pixel connectivity来实现这一目标。它可以正常工作,但是填充时间很长(Firefox多次询问我停止脚本。Chrome通常只是因为超过最大调用堆栈而终止了该脚本)。我的问题:如何优化代码或应使用其他算法?
PS:我更正了Wan Chap提到的错误。
编辑:我现在添加了一个全局变量canImageData
并更改了getRGBStringFromPixel()
函数。现在,填充工作的速度要快得多,但是我一直不断超出呼叫堆栈错误,导致更大的区域仅被部分填充。有人有上述算法的javascript适合版本的示例吗?
编辑的最后:我发现了另一个性能更高的解决方案,它在另一个站点的js源中进行了挖掘。我在下面将解决方案作为答案发布。
我的代码:
var can = document.getElementById('can');
var ctx = can.getContext('2d');
var canImageData = null;
var RGB_STRING_WHITE = '255-255-255-255';
function colour_fill_4_connectivity(x, y, RGBString_fill_colour, RGBString_region_colour){
try {
var currentPixelRGBString = getRGBStringFromPixel(x, y);
if(currentPixelRGBString == RGBString_fill_colour || currentPixelRGBString != RGBString_region_colour)
return;
setPixelFromRGBString(x, y, RGBString_fill_colour);
if(x < can.width)
colour_fill_4_connectivity(x + 1, y, RGBString_fill_colour, RGBString_region_colour);
if(x > 0)
colour_fill_4_connectivity(x - 1, y, RGBString_fill_colour, RGBString_region_colour);
if(y < can.height)
colour_fill_4_connectivity(x, y + 1, RGBString_fill_colour, RGBString_region_colour);
if(y > 0)
colour_fill_4_connectivity(x, y - 1, RGBString_fill_colour, RGBString_region_colour);
} catch(e) {
console.log('ERROR: colour_fill_4_connectivity(' + x + ', ' + y + ', ' + RGBString_fill_colour + ', ' + RGBString_region_colour + ') -> ' + e);
}
}
function fillColor(x, y, RGBString_fill_colour, RGBString_region_colour) {
colour_fill_4_connectivity(x, y, RGBString_fill_colour, RGBString_region_colour);
}
function RGBStringToArray(valRGB) {
return valRGB.split('-');
}
function getRGBStringFromPixel(x, y) {
var data = canImageData.data;
var startIndex = (x + y * can.width) * 4;
return data[startIndex] + '-' + data[startIndex + 1] + '-' + data[startIndex + 2] + '-' + data[startIndex + 3];
}
function setPixelFromRGBString(x, y, valRGB) {
var imageData = ctx.createImageData(1, 1);
var data = imageData.data;
var rgbArr = RGBStringToArray(valRGB);
var startIndex = (x + y * can.width) * 4;
data[0] = rgbArr[0];
data[1] = rgbArr[1];
data[2] = rgbArr[2];
data[3] = rgbArr[3];
ctx.putImageData(imageData, x, y);
canImageData.data[startIndex] = rgbArr[0];
canImageData.data[startIndex + 1] = rgbArr[1];
canImageData.data[startIndex + 2] = rgbArr[2];
canImageData.data[startIndex + 3] = rgbArr[3];
}
// TEST
function _test_getRGBStringFromPixel() {
var res = [];
for(var y = 0; y < can.height; y++) {
for(var x = 0; x < can.width; x++) {
res.push({x:x, y:y, color:getRGBStringFromPixel(x, y)});
}
}
return JSON.stringify(res);
}
var img = new Image();
img.onload = (function(e) {
can.width = this.width;
can.height = this.height;
ctx.drawImage(this, 0, 0);
canImageData = ctx.getImageData(0, 0, can.width, can.height);
}).bind(img);
img.src = 'test-image.png';
can.addEventListener('click', function(e) {
var clickX = e.clientX, clickY = e.clientY;
if(e.button === 0 && clickX <= can.width && clickY <= can.height)
fillColor(clickX, clickY, RGB_STRING_WHITE, getRGBStringFromPixel(clickX, clickY));
}, false);
body {
padding: 0;
margin: 0;
}
<canvas id="can" width="600" height="400"></canvas>
答案 0 :(得分:0)
函数data[2] = rgbArr[2];
似乎只是一个小错误:
将var can = document.getElementById('can');
var ctx = can.getContext('2d');
var RGB_STRING_WHITE = '255-255-255-255';
function colour_fill_4_connectivity(x, y, RGBString_fill_colour, RGBString_region_colour) {
//try {
var currentPixelRGBString = getRGBStringFromPixel(x, y);
if (currentPixelRGBString == RGBString_fill_colour || currentPixelRGBString != RGBString_region_colour)
return;
setPixelFromRGBString(x, y, RGBString_fill_colour);
if (x > 0)
colour_fill_4_connectivity(x - 1, y, RGBString_fill_colour, RGBString_region_colour);
if (x < can.width)
colour_fill_4_connectivity(x + 1, y, RGBString_fill_colour, RGBString_region_colour);
if (y > 0)
colour_fill_4_connectivity(x, y - 1, RGBString_fill_colour, RGBString_region_colour);
if (y < can.height)
colour_fill_4_connectivity(x, y + 1, RGBString_fill_colour, RGBString_region_colour);
//} catch(e) {
//console.log('ERROR: colour_fill_4_connectivity(' + x + ', ' + y + ', ' + RGBString_fill_colour + ', ' + RGBString_region_colour + ') -> ' + e);
//}
}
function RGBStringToArray(valRGB) {
return valRGB.split('-');
}
function getRGBStringFromPixel(x, y) {
var data = ctx.getImageData(x, y, x + 1, y + 1).data;
return data[0] + '-' + data[1] + '-' + data[2] + '-' + data[3];
}
function setPixelFromRGBString(x, y, valRGB) {
var imageData = ctx.createImageData(1, 1);
var data = imageData.data;
var rgbArr = RGBStringToArray(valRGB);
data[0] = rgbArr[0];
data[1] = rgbArr[1];
data[1] = rgbArr[2];
data[3] = rgbArr[3];
ctx.putImageData(imageData, x, y);
}
// TEST
var img = new Image();
img.onload = (function(e) {
ctx.drawImage(img, 0, 0);
}).bind(img);
img.src = 'https://static1.squarespace.com/static/593357e715d5dbea570d2118/593ee768893fc0375f9c6fd5/5c7499a7e79c707a50772134/1551194769831/Test+pattern.png?format=1000w';
can.addEventListener('click', function(e) {
var clickX = e.clientX,
clickY = e.clientY;
if (e.button === 0 && clickX <= can.width && clickY <= can.height)
colour_fill_4_connectivity(clickX, clickY, RGB_STRING_WHITE, getRGBStringFromPixel(clickX, clickY));
}, false);
更改为body {
padding: 0;
margin: 0;
}
<canvas id="can" width="600" height="400"></canvas>
(?s)\([^(]*\((.+)\)[^)]*\)
(?s)
答案 1 :(得分:0)
经过研究,我决定深入研究http://skribbl.io的来源,该来源具有可靠且快速的填充工具。他们找到了一个迭代的解决方案。我从源文件中提取了一个代码片段,并更改了部分以使其更易于阅读:
function DrawingBoard(canvas) {
this.canvas = canvas;
this.canvasCtx = this.canvas.getContext("2d");
}
DrawingBoard.prototype.getPixel = function(imageData, x, y) {
var startIndex = 4 * (y * imageData.width + x);
return startIndex >= 0 && startIndex < imageData.data.length ? [imageData.data[startIndex], imageData.data[startIndex + 1], imageData.data[startIndex + 2]] : [0, 0, 0]
}
DrawingBoard.prototype.setPixel = function(imageData, startIndex, r, g, b) {
startIndex >= 0 && startIndex < imageData.data.length && (imageData.data[startIndex] = r, imageData.data[startIndex + 1] = g, imageData.data[startIndex + 2] = b, imageData.data[startIndex + 3] = 255)
}
DrawingBoard.prototype.floodFill = function(startX, startY, r, g, b) {
var imageData = this.canvasCtx.getImageData(0, 0, this.canvas.width, this.canvas.height), points = [[startX, startY]], targetPixelRGB = this.getPixel(imageData, startX, startY);
if (r != targetPixelRGB[0] || g != targetPixelRGB[1] || b != targetPixelRGB[2]) {
for (
var c = function(t) {
var e = imageData.data[t],
i = imageData.data[t + 1],
c = imageData.data[t + 2];
if (e == r && i == g && c == b) return false;
var u = Math.abs(e - targetPixelRGB[0]),
h = Math.abs(i - targetPixelRGB[1]),
l = Math.abs(c - targetPixelRGB[2]);
return u < 1 && h < 1 && l < 1
},
imageHeight = imageData.height, imageHeightWidth = imageData.width; points.length;
) {
var point, pointX, pointY, index, y, m;
for (point = points.pop(), pointX = point[0], pointY = point[1], index = 4 * (pointY * imageHeightWidth + pointX); pointY-- >= 0 && c(index);) {
index -= 4 * imageHeightWidth;
}
for (index += 4 * imageHeightWidth, ++pointY, y = 0, m = 0; pointY++ < imageHeight - 1 && c(index);) {
this.setPixel(imageData, index, r, g, b);
pointX > 0;
c(index - 4) ? y || (points.push([pointX - 1, pointY]), y = 1) : y && (y = 0);
pointX < imageHeightWidth - 1;
c(index + 4) ? m || (points.push([pointX + 1, pointY]), m = 1) : m && (m = 0);
index += 4 * imageHeightWidth;
}
}
this.canvasCtx.putImageData(imageData, 0, 0)
}
}
// TEST
var db = new DrawingBoard(document.getElementById('can'));
var img = new Image();
var fillColor = [255, 0, 255];
img.onload = (function() {
db.canvasCtx.drawImage(this, 0, 0);
}).bind(img);
img.src = 'test.png';
addEventListener('click', function(e) {
var cx = e.clientX, cy = e.clientY;
if(
e.button === 0 &&
cx > 0 && cx < db.canvas.width &&
cy > 0 && cy < db.canvas.height
) {
db.floodFill(cx, cy, fillColor[0], fillColor[1], fillColor[2]);
}
}, false);
body {
padding: 0;
margin: 0;
}
<canvas id="can" width="600" height="400"></canvas>
工作小提琴可以在这里找到:https://jsfiddle.net/f0kuwa5e/