Support patterns for Fill With Color
parent
9443418480
commit
14cf228157
3
TODO.md
3
TODO.md
|
@ -143,8 +143,7 @@ SVG (or HTML?) with invisible selectable transformed text elements?
|
|||
|
||||
* Shape Styles
|
||||
* Shapes: respond to Ctrl (It's complicated)
|
||||
* Handle patterns (black and white mode)
|
||||
* Still needed for fill tool
|
||||
* Patterns (black and white mode, winter theme)
|
||||
* Check to make sure patterns are aligned properly for all the tools
|
||||
* There's supposed to be a mapping between color values and pattern fills, used by the text tool and for the palette when switching between modes (colors should be kept between going to black and white mode and back)
|
||||
|
||||
|
|
|
@ -358,6 +358,113 @@ function draw_fill(ctx, start_x, start_y, fill_r, fill_g, fill_b, fill_a){
|
|||
}
|
||||
}
|
||||
|
||||
function draw_fill_supporting_patterns(ctx, start_x, start_y, swatch) {
|
||||
const source_canvas = ctx.canvas;
|
||||
const fill_canvas = make_canvas(source_canvas.width, source_canvas.height);
|
||||
draw_fill_separately(source_canvas.ctx, fill_canvas.ctx, start_x, start_y, 255, 255, 255, 255);
|
||||
replace_colors_with_swatch(fill_canvas.ctx, swatch, 0, 0);
|
||||
ctx.drawImage(fill_canvas, 0, 0);
|
||||
}
|
||||
|
||||
function draw_fill_separately(source_ctx, dest_ctx, start_x, start_y, fill_r, fill_g, fill_b, fill_a){
|
||||
const stack = [[start_x, start_y]];
|
||||
const c_width = source_ctx.canvas.width;
|
||||
const c_height = source_ctx.canvas.height;
|
||||
const source_id = source_ctx.getImageData(0, 0, c_width, c_height);
|
||||
const dest_id = dest_ctx.getImageData(0, 0, c_width, c_height);
|
||||
let pixel_pos = (start_y*c_width + start_x) * 4;
|
||||
const start_r = source_id.data[pixel_pos+0];
|
||||
const start_g = source_id.data[pixel_pos+1];
|
||||
const start_b = source_id.data[pixel_pos+2];
|
||||
const start_a = source_id.data[pixel_pos+3];
|
||||
|
||||
// if(
|
||||
// fill_r === start_r &&
|
||||
// fill_g === start_g &&
|
||||
// fill_b === start_b &&
|
||||
// fill_a === start_a
|
||||
// ){
|
||||
// return;
|
||||
// }
|
||||
|
||||
while(stack.length){
|
||||
let new_pos;
|
||||
let x;
|
||||
let y;
|
||||
let reach_left;
|
||||
let reach_right;
|
||||
new_pos = stack.pop();
|
||||
x = new_pos[0];
|
||||
y = new_pos[1];
|
||||
|
||||
pixel_pos = (y*c_width + x) * 4;
|
||||
while(matches_start_color(pixel_pos)){
|
||||
y--;
|
||||
pixel_pos = (y*c_width + x) * 4;
|
||||
}
|
||||
reach_left = false;
|
||||
reach_right = false;
|
||||
// eslint-disable-next-line no-constant-condition
|
||||
while(true){
|
||||
y++;
|
||||
pixel_pos = (y*c_width + x) * 4;
|
||||
|
||||
if(!(y < c_height && matches_start_color(pixel_pos))){
|
||||
break;
|
||||
}
|
||||
|
||||
color_pixel(pixel_pos);
|
||||
|
||||
if(x > 0){
|
||||
if(matches_start_color(pixel_pos - 4)){
|
||||
if(!reach_left){
|
||||
stack.push([x - 1, y]);
|
||||
reach_left = true;
|
||||
}
|
||||
}else if(reach_left){
|
||||
reach_left = false;
|
||||
}
|
||||
}
|
||||
|
||||
if(x < c_width-1){
|
||||
if(matches_start_color(pixel_pos + 4)){
|
||||
if(!reach_right){
|
||||
stack.push([x + 1, y]);
|
||||
reach_right = true;
|
||||
}
|
||||
}else if(reach_right){
|
||||
reach_right = false;
|
||||
}
|
||||
}
|
||||
|
||||
pixel_pos += c_width * 4;
|
||||
}
|
||||
}
|
||||
dest_ctx.putImageData(dest_id, 0, 0);
|
||||
|
||||
// TODO: rename function
|
||||
function matches_start_color(pixel_pos){
|
||||
return (
|
||||
// NOT REACHED YET
|
||||
dest_id.data[pixel_pos+3] === 0 &&
|
||||
// and matches start color (i.e. area to fill)
|
||||
(
|
||||
source_id.data[pixel_pos+0] === start_r &&
|
||||
source_id.data[pixel_pos+1] === start_g &&
|
||||
source_id.data[pixel_pos+2] === start_b &&
|
||||
source_id.data[pixel_pos+3] === start_a
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function color_pixel(pixel_pos){
|
||||
dest_id.data[pixel_pos+0] = fill_r;
|
||||
dest_id.data[pixel_pos+1] = fill_g;
|
||||
dest_id.data[pixel_pos+2] = fill_b;
|
||||
dest_id.data[pixel_pos+3] = fill_a;
|
||||
}
|
||||
}
|
||||
|
||||
function draw_noncontiguous_fill(ctx, x, y, fill_r, fill_g, fill_b, fill_a){
|
||||
|
||||
const c_width = canvas.width;
|
||||
|
|
17
src/tools.js
17
src/tools.js
|
@ -366,25 +366,28 @@ window.tools = [{
|
|||
description: "Fills an area with the selected drawing color.",
|
||||
cursor: ["fill-bucket", [8, 22], "crosshair"],
|
||||
pointerdown(ctx, x, y) {
|
||||
|
||||
// Get the rgba values of the selected fill color
|
||||
const rgba = get_rgba_from_color(fill_color);
|
||||
|
||||
if(shift){
|
||||
undoable({
|
||||
name: "Replace Color",
|
||||
icon: get_icon_for_tool(this),
|
||||
}, ()=> {
|
||||
// Perform global color replacement
|
||||
draw_noncontiguous_fill(ctx, x, y, rgba[0], rgba[1], rgba[2], rgba[3]);
|
||||
// TODO: support patterns
|
||||
const fill_rgba = get_rgba_from_color(fill_color);
|
||||
draw_noncontiguous_fill(ctx, x, y, fill_rgba[0], fill_rgba[1], fill_rgba[2], fill_rgba[3]);
|
||||
});
|
||||
} else {
|
||||
undoable({
|
||||
name: "Fill With Color",
|
||||
icon: get_icon_for_tool(this),
|
||||
}, ()=> {
|
||||
// Perform a normal fill operation
|
||||
draw_fill(ctx, x, y, rgba[0], rgba[1], rgba[2], rgba[3]);
|
||||
if (fill_color instanceof CanvasPattern || fill_color instanceof CanvasGradient) {
|
||||
draw_fill_supporting_patterns(ctx, x, y, fill_color);
|
||||
} else {
|
||||
// Perform a normal fill operation
|
||||
const fill_rgba = get_rgba_from_color(fill_color);
|
||||
draw_fill(ctx, x, y, fill_rgba[0], fill_rgba[1], fill_rgba[2], fill_rgba[3]);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue