-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathfloodfill.js
97 lines (80 loc) · 2.64 KB
/
floodfill.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
// https://codepen.io/Geeyoam/pen/vLGZzG
function getColorAtPixel(imageData, x, y) {
const {width, data} = imageData;
return {
r: data[4 * (width * y + x) + 0],
g: data[4 * (width * y + x) + 1],
b: data[4 * (width * y + x) + 2],
a: data[4 * (width * y + x) + 3]
};
}
function setColorAtPixel(imageData, color, x, y) {
const {width, data} = imageData;
data[4 * (width * y + x) + 0] = color.r & 0xff;
data[4 * (width * y + x) + 1] = color.g & 0xff;
data[4 * (width * y + x) + 2] = color.b & 0xff;
data[4 * (width * y + x) + 3] = color.a & 0xff;
}
function colorMatch(a, b) {
return a.r === b.r && a.g === b.g && a.b === b.b && a.a === b.a;
}
function floodFill(imageData, newColor, x, y) {
const {width, height, data} = imageData;
const stack = [];
const baseColor = getColorAtPixel(imageData, x, y);
let operator = {x, y};
// Check if base color and new color are the same
if(colorMatch(baseColor, newColor) ) {
return;
}
// Add the clicked location to stack
stack.push({x: operator.x, y: operator.y});
while(stack.length) {
operator = stack.pop();
let contiguousDown = true; // Vertical is assumed to be true
let contiguousUp = true; // Vertical is assumed to be true
let contiguousLeft = false;
let contiguousRight = false;
// Move to top most contiguousDown pixel
while(contiguousUp && operator.y >= 0) {
operator.y--;
contiguousUp = colorMatch(getColorAtPixel(imageData, operator.x, operator.y), baseColor);
}
// Move downward
while(contiguousDown && operator.y < height) {
setColorAtPixel(imageData, newColor, operator.x, operator.y)
// Check left
if(operator.x - 1 >= 0 && colorMatch(getColorAtPixel(imageData, operator.x - 1, operator.y), baseColor) ) {
if(!contiguousLeft) {
contiguousLeft = true;
stack.push({x: operator.x - 1, y: operator.y});
}
}
else {
contiguousLeft = false;
}
// Check right
if(operator.x + 1 < width && colorMatch(getColorAtPixel(imageData, operator.x + 1, operator.y), baseColor) ) {
if(!contiguousRight) {
stack.push({x: operator.x + 1, y: operator.y});
contiguousRight = true;
}
}
else {
contiguousRight = false;
}
operator.y++;
contiguousDown = colorMatch(getColorAtPixel(imageData, operator.x, operator.y), baseColor);
}
}
}
function doFill(evt) {
if(currentTool != 'fill') return false;
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
const col = getColorForFloodFill();
const rect = canvas.getBoundingClientRect();
const x = Math.round(evt.clientX - rect.left);
const y = Math.round(evt.clientY - rect.top);
floodFill(imageData, col, x, y);
ctx.putImageData(imageData, 0, 0);
}