英文:
Flood fill for html5 canvas not working as expected
问题
I have a flood fill I'm using from another post on here.
我正在使用从这里的另一篇帖子中获取的泛洪填充。
I removed all the alpha channels but the colors are off.
我移除了所有 alpha 通道,但颜色不正确。
I've racked my brain for days changing things around and using different fill methods but this one has gotten me closest.
我已经苦思冥想了几天,不断尝试不同的方法和不同的填充方法,但这个方法让我离解决问题最近。
I'll be having users draw and fill as they please.
我将让用户随意绘制和填充。
Sorry if this is something simple.
如果这是一些简单的东西,我很抱歉。
Only color that works is red and white.
唯一有效的颜色是红色和白色。
I don't know what is causing the issue, if I try to pass in rgb value black (0,0,0) it crashes.
我不知道是什么导致了问题,如果我尝试传递RGB值黑色(0,0,0),它会崩溃。
英文:
I have a flood fill I'm using from another post on here.
I removed all the alpha channels but the colors are off.
I'v racked my brain for days changing things around and using different fill methods but this one has gotten me closest. I'll be having users draw and fill as they please. Sorry if this is something simple. Only color that works is red and white.
<canvas id="myCanvas" width="300" height="150" style="border:1px solid grey"></canvas>
<script>
const c = document.getElementById("myCanvas");
const ctx = c.getContext("2d");
ctx.strokeStyle = "rgb(252, 53, 3)";
ctx.strokeRect(10, 10, 50, 50);
var red = [252, 53, 3];
floodFill(ctx, 15, 15, red);
ctx.strokeStyle = "rgb(3, 252, 3)";
ctx.strokeRect(75, 10, 50, 50);
var green = [3, 252, 3];
floodFill(ctx, 80, 15, green);
ctx.strokeStyle = "rgb(3, 119, 252)";
ctx.strokeRect(150, 10, 50, 50);
var blue = [3, 119, 252];
floodFill(ctx, 155, 15, blue);
ctx.strokeStyle = "rgb(0, 0, 0)";
ctx.strokeRect(225, 10, 50, 50);
var black = [0, 0, 0];
//floodFill(ctx, 230, 15, black);
//black get's stackoverflow and crashes page
function getPixel(imageData, x, y) {
if (x < 0 || y < 0 || x >= imageData.width || y >= imageData.height) {
return [-1, -1, -1, -1]; // impossible color
} else {
const offset = (y * imageData.width + x) * 4;
var pixel = imageData.data.slice(offset, offset + 4);
pixel[3] = 1; // ignore alpha
return pixel;
}
}
function setPixel(imageData, x, y, color) {
const offset = (y * imageData.width + x) * 4;
imageData.data[offset + 0] = color[0];
imageData.data[offset + 1] = color[1];
imageData.data[offset + 2] = color[2];
imageData.data[offset + 3] = color[0];
}
function colorsMatch(a, b, rSq) {
if (a === b) return true;
if (a == null || b == null) return false;
if (a.length !== b.length) return false;
for (var i = 0; i < a.length; ++i) {
if (a[i] !== b[i]) return false;
}
return true;
}
function floodFill(ctx, x, y, fillColor, range = 1) {
// read the pixels in the canvas
const imageData = ctx.getImageData(0, 0, ctx.canvas.width, ctx.canvas.height);
// flags for if we visited a pixel already
const visited = new Uint8Array(imageData.width, imageData.height);
// get the color we're filling
const targetColor = getPixel(imageData, x, y);
console.log("target: " + targetColor + "Fill: " + fillColor);
// check we are actually filling a different color
if (!colorsMatch(targetColor, fillColor)) {
const rangeSq = range * range;
const pixelsToCheck = [x, y];
while (pixelsToCheck.length > 0) {
const y = pixelsToCheck.pop();
const x = pixelsToCheck.pop();
const currentColor = getPixel(imageData, x, y);
if (!visited[y * imageData.width + x] && colorsMatch(currentColor, targetColor, rangeSq)) {
setPixel(imageData, x, y, fillColor);
visited[y * imageData.width + x] = 1; // mark we were here already
pixelsToCheck.push(x + 1, y);
pixelsToCheck.push(x - 1, y);
pixelsToCheck.push(x, y + 1);
pixelsToCheck.push(x, y - 1);
}
}
// put the data back
ctx.putImageData(imageData, 0, 0);
}
}
</script>
I don't know what is causing the issue, if I try to pass in rgb value black (0,0,0) it crashes.
答案1
得分: 0
要获得完全不透明的 alpha 通道,你需要将第4个条目设置为 255
,而不是 1
(在 getPixel
中),也不要将其设置为红色通道值(在 setPixel
中)。
但是,如果你在 getPixel
中忽略 alpha 值但仍允许透明度,那么原始的黑色透明像素(rgba(0, 0, 0, 0)
)将被视为与完全不透明的黑色像素(rgb(0, 0, 0)
)相同的颜色,你的代码不会对其进行更改。因此,要么在上下文中完全禁用透明度(例如,绘制一个完全不透明的白色矩形),要么在 getPixel
函数中考虑 alpha 值。
以下是你的代码的一部分,已被翻译:
const c = document.getElementById("myCanvas");
const ctx = c.getContext("2d");
/**
* 将透明的黑色像素转换为不透明的白色像素。
*/
ctx.fillStyle = "white";
ctx.fillRect(0, 0, c.width, c.height);
ctx.strokeStyle = "rgb(252, 53, 3)";
ctx.strokeRect(10, 10, 50, 50);
var red = [252, 53, 3];
floodFill(ctx, 15, 15, red);
ctx.strokeStyle = "rgb(3, 252, 3)";
ctx.strokeRect(75, 10, 50, 50);
var green = [3, 252, 3];
floodFill(ctx, 80, 15, green);
ctx.strokeStyle = "rgb(3, 119, 252)";
ctx.strokeRect(150, 10, 50, 50);
var blue = [3, 119, 252];
floodFill(ctx, 155, 15, blue);
ctx.strokeStyle = "rgb(0, 0, 0)";
ctx.strokeRect(225, 10, 50, 50);
var black = [0, 0, 0];
floodFill(ctx, 230, 15, black);
function getPixel(imageData, x, y) {
if (x < 0 || y < 0 || x >= imageData.width || y >= imageData.height) {
return [-1, -1, -1, -1]; // 不可能的颜色
} else {
const offset = (y * imageData.width + x) * 4;
var pixel = imageData.data.slice(offset, offset + 4);
pixel[3] = 1; // 忽略 alpha
return pixel;
}
}
function setPixel(imageData, x, y, color) {
const offset = (y * imageData.width + x) * 4;
imageData.data[offset + 0] = color[0];
imageData.data[offset + 1] = color[1];
imageData.data[offset + 2] = color[2];
imageData.data[offset + 3] = 255; // 255 是完全不透明的
}
// 其余部分未翻译
请注意,这只是你代码的一部分,其他部分未进行翻译。
英文:
To get full alpha you need to set the 4th items to 255
instead of 1
(in getPixel
), nor to the red channel value (in setPixel
).
However, if you do ignore the alpha value in getPixel
but still allow transparency, then the original black transparent pixel (rgba(0, 0, 0, 0)
) will be seen as being the same color as a fully opaque black pixel (rgb(0, 0, 0)
), and your code won't change it. So either disable transparency entirely on the context (e.g. draw a fully opaque white rectangle),
<!-- begin snippet: js hide: true console: true babel: false -->
<!-- language: lang-js -->
const c = document.getElementById("myCanvas");
const ctx = c.getContext("2d");
/**
* Convert transparent black pixels
* to opaque white ones.
*/
ctx.fillStyle = "white";
ctx.fillRect(0, 0, c.width, c.height);
ctx.strokeStyle = "rgb(252, 53, 3)";
ctx.strokeRect(10, 10, 50, 50);
var red = [252, 53, 3];
floodFill(ctx, 15, 15, red);
ctx.strokeStyle = "rgb(3, 252, 3)";
ctx.strokeRect(75, 10, 50, 50);
var green = [3, 252, 3];
floodFill(ctx, 80, 15, green);
ctx.strokeStyle = "rgb(3, 119, 252)";
ctx.strokeRect(150, 10, 50, 50);
var blue = [3, 119, 252];
floodFill(ctx, 155, 15, blue);
ctx.strokeStyle = "rgb(0, 0, 0)";
ctx.strokeRect(225, 10, 50, 50);
var black = [0, 0, 0];
floodFill(ctx, 230, 15, black);
function getPixel(imageData, x, y) {
if (x < 0 || y < 0 || x >= imageData.width || y >= imageData.height) {
return [-1, -1, -1, -1]; // impossible color
} else {
const offset = (y * imageData.width + x) * 4;
var pixel = imageData.data.slice(offset, offset + 4);
pixel[3] = 1; // ignore alpha
return pixel;
}
}
function setPixel(imageData, x, y, color) {
const offset = (y * imageData.width + x) * 4;
imageData.data[offset + 0] = color[0];
imageData.data[offset + 1] = color[1];
imageData.data[offset + 2] = color[2];
imageData.data[offset + 3] = 255; // 255 is fully opaque
}
function colorsMatch(a, b, rSq) {
if (a === b) return true;
if (a == null || b == null) return false;
if (a.length !== b.length) return false;
for (var i = 0; i < a.length; ++i) {
if (a[i] !== b[i]) return false;
}
return true;
}
function floodFill(ctx, x, y, fillColor, range = 1) {
// read the pixels in the canvas
const imageData = ctx.getImageData(0, 0, ctx.canvas.width, ctx.canvas.height);
// flags for if we visited a pixel already
const visited = new Uint8Array(imageData.width, imageData.height);
// get the color we're filling
const targetColor = getPixel(imageData, x, y);
console.log("target: " + targetColor + "Fill: " + fillColor);
// check we are actually filling a different color
if (!colorsMatch(targetColor, fillColor)) {
const rangeSq = range * range;
const pixelsToCheck = [x, y];
while (pixelsToCheck.length > 0) {
const y = pixelsToCheck.pop();
const x = pixelsToCheck.pop();
const currentColor = getPixel(imageData, x, y);
if (!visited[y * imageData.width + x] && colorsMatch(currentColor, targetColor, rangeSq)) {
setPixel(imageData, x, y, fillColor);
visited[y * imageData.width + x] = 1; // mark we were here already
pixelsToCheck.push(x + 1, y);
pixelsToCheck.push(x - 1, y);
pixelsToCheck.push(x, y + 1);
pixelsToCheck.push(x, y - 1);
}
}
// put the data back
ctx.putImageData(imageData, 0, 0);
}
}
<!-- language: lang-html -->
<canvas id="myCanvas" width="300" height="150" style="border:1px solid grey"></canvas>
<!-- end snippet -->
or let the alpha in the getPixel
function.
<!-- begin snippet: js hide: true console: true babel: false -->
<!-- language: lang-js -->
const c = document.getElementById("myCanvas");
const ctx = c.getContext("2d");
ctx.strokeStyle = "rgb(252, 53, 3)";
ctx.strokeRect(10, 10, 50, 50);
var red = [252, 53, 3];
floodFill(ctx, 15, 15, red);
ctx.strokeStyle = "rgb(3, 252, 3)";
ctx.strokeRect(75, 10, 50, 50);
var green = [3, 252, 3];
floodFill(ctx, 80, 15, green);
ctx.strokeStyle = "rgb(3, 119, 252)";
ctx.strokeRect(150, 10, 50, 50);
var blue = [3, 119, 252];
floodFill(ctx, 155, 15, blue);
ctx.strokeStyle = "rgb(0, 0, 0)";
ctx.strokeRect(225, 10, 50, 50);
var black = [0, 0, 0];
floodFill(ctx, 230, 15, black);
//black get's stackoverflow and crashes page
function getPixel(imageData, x, y) {
if (x < 0 || y < 0 || x >= imageData.width || y >= imageData.height) {
return [-1, -1, -1, -1]; // impossible color
} else {
const offset = (y * imageData.width + x) * 4;
var pixel = imageData.data.slice(offset, offset + 4);
// Do not ignore alpha
return pixel;
}
}
function setPixel(imageData, x, y, color) {
const offset = (y * imageData.width + x) * 4;
imageData.data[offset + 0] = color[0];
imageData.data[offset + 1] = color[1];
imageData.data[offset + 2] = color[2];
imageData.data[offset + 3] = 255; // 255 is fully opaque
}
function colorsMatch(a, b, rSq) {
if (a === b) return true;
if (a == null || b == null) return false;
if (a.length !== b.length) return false;
for (var i = 0; i < a.length; ++i) {
if (a[i] !== b[i]) return false;
}
return true;
}
function floodFill(ctx, x, y, fillColor, range = 1) {
// read the pixels in the canvas
const imageData = ctx.getImageData(0, 0, ctx.canvas.width, ctx.canvas.height);
// flags for if we visited a pixel already
const visited = new Uint8Array(imageData.width, imageData.height);
// get the color we're filling
const targetColor = getPixel(imageData, x, y);
console.log("target: " + targetColor + "Fill: " + fillColor);
// check we are actually filling a different color
if (!colorsMatch(targetColor, fillColor)) {
const rangeSq = range * range;
const pixelsToCheck = [x, y];
while (pixelsToCheck.length > 0) {
const y = pixelsToCheck.pop();
const x = pixelsToCheck.pop();
const currentColor = getPixel(imageData, x, y);
if (!visited[y * imageData.width + x] && colorsMatch(currentColor, targetColor, rangeSq)) {
setPixel(imageData, x, y, fillColor);
visited[y * imageData.width + x] = 1; // mark we were here already
pixelsToCheck.push(x + 1, y);
pixelsToCheck.push(x - 1, y);
pixelsToCheck.push(x, y + 1);
pixelsToCheck.push(x, y - 1);
}
}
// put the data back
ctx.putImageData(imageData, 0, 0);
}
}
<!-- language: lang-html -->
<canvas id="myCanvas" width="300" height="150" style="border:1px solid grey"></canvas>
<!-- end snippet -->
Though, note that I didn't check your algorithm at all, so it may contain other issues.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论