diff --git a/src/components/tool-options-panel/tool-options-fill/messages.json b/src/components/tool-options-panel/tool-options-fill/messages.json index 2fe7598..d2a99d7 100644 --- a/src/components/tool-options-panel/tool-options-fill/messages.json +++ b/src/components/tool-options-panel/tool-options-fill/messages.json @@ -2,6 +2,8 @@ "en-US": { "fill": "Fill", "smartFill": "Use smart fill", - "smartFillExpl": "When using smart fill, the area surrounding the fill origin will be filled until a boundary has been recognized." + "smartFillExpl": "When using smart fill, the area surrounding the fill origin will be filled until a boundary has been recognized.", + "feather": "Feather", + "threshold": "Threshold" } } diff --git a/src/components/tool-options-panel/tool-options-fill/tool-options-fill.vue b/src/components/tool-options-panel/tool-options-fill/tool-options-fill.vue index 0af68d1..c556cb5 100644 --- a/src/components/tool-options-panel/tool-options-fill/tool-options-fill.vue +++ b/src/components/tool-options-panel/tool-options-fill/tool-options-fill.vue @@ -1,7 +1,7 @@ /** * The MIT License (MIT) * - * Igor Zinken 2022 - https://www.igorski.nl + * Igor Zinken 2022-2026 - https://www.igorski.nl * * Permission is hereby granted, free of charge, to any person obtaining a copy of * this software and associated documentation files (the "Software"), to deal in @@ -32,18 +32,41 @@ />

+
+ + +
+
+ + +
diff --git a/src/definitions/editor.ts b/src/definitions/editor.ts index b923cc4..800d6e9 100644 --- a/src/definitions/editor.ts +++ b/src/definitions/editor.ts @@ -136,6 +136,8 @@ export type SelectionToolOptions = { export type FillToolOptions = { smartFill: boolean; + feather: number; + threshold: number; }; export type WandToolOptions = { diff --git a/src/rendering/actors/layer-renderer.ts b/src/rendering/actors/layer-renderer.ts index 7c80337..46232b2 100644 --- a/src/rendering/actors/layer-renderer.ts +++ b/src/rendering/actors/layer-renderer.ts @@ -355,9 +355,10 @@ export default class LayerRenderer extends ZoomableSprite { if ( this.toolOptions.smartFill ) { // we need to translate pointer offset to match the relative, untransformed source layer content const point = rotatePointer( this._pointer, this.layer, width, height ); - floodFill( ctx, point.x, point.y, color ); + const { fillOptions } = this.getStore().getters; + floodFill( ctx, point.x, point.y, color, fillOptions.feather, fillOptions.threshold ); } else { - ctx.fillStyle = this.getStore().getters.activeColor; + ctx.fillStyle = color; if ( selection ) { ctx.fill(); } else { diff --git a/src/rendering/operations/fill.ts b/src/rendering/operations/fill.ts index 7838561..17833f1 100644 --- a/src/rendering/operations/fill.ts +++ b/src/rendering/operations/fill.ts @@ -1,7 +1,7 @@ /** * The MIT License (MIT) * - * Igor Zinken 2022-2023 - https://www.igorski.nl + * Igor Zinken 2022-2026 - https://www.igorski.nl * * Permission is hereby granted, free of charge, to any person obtaining a copy of * this software and associated documentation files (the "Software"), to deal in @@ -35,10 +35,12 @@ const TWO_PI = Math.PI * 2; * @param {Number} sourceY y-coordinate of the fill origin * @param {String} fillColor RGBA String value for the fill color * @param {Number=} feather optional amount of pixels at edges to fill (less aliased result) + * @param {Number=} threshold optional threshold to apply to the color selection */ -export const floodFill = ( ctx: CanvasRenderingContext2D, sourceX: number, sourceY: number, - fillColor: string, feather = 5 ): void => { - const path = selectByColor( ctx.canvas, sourceX, sourceY ); +export const floodFill = ( + ctx: CanvasRenderingContext2D, sourceX: number, sourceY: number, fillColor: string, feather = 5, threshold = 0, +): void => { + const path = selectByColor( ctx.canvas, sourceX, sourceY, threshold ); ctx.strokeStyle = fillColor; ctx.fillStyle = fillColor; diff --git a/src/store/modules/editor-module.ts b/src/store/modules/editor-module.ts index f4f21a4..2fe90cf 100644 --- a/src/store/modules/editor-module.ts +++ b/src/store/modules/editor-module.ts @@ -63,7 +63,7 @@ export const createEditorState = ( props?: Partial ): EditorState = [ ToolTypes.ERASER ]: { size: 10, type: BrushTypes.PAINT_BRUSH, opacity: 1, thickness: .5 }, [ ToolTypes.CLONE ] : { size: 10, type: BrushTypes.PAINT_BRUSH, opacity: .75, thickness: .5, sourceLayerId: TOOL_SRC_MERGED, coords: null }, [ ToolTypes.SELECTION ] : { lockRatio: false, xRatio: 1, yRatio: 1 }, - [ ToolTypes.FILL ] : { smartFill: true }, + [ ToolTypes.FILL ] : { smartFill: true, feather: 5, threshold: 0 }, [ ToolTypes.WAND ] : { threshold: 50, sampleMerged: true }, }, snapAlign : true, diff --git a/tests/unit/store/modules/editor-module.spec.ts b/tests/unit/store/modules/editor-module.spec.ts index aaebb31..dc4626f 100644 --- a/tests/unit/store/modules/editor-module.spec.ts +++ b/tests/unit/store/modules/editor-module.spec.ts @@ -17,7 +17,7 @@ describe( "Vuex editor module", () => { [ ToolTypes.ERASER ]: { size: 10, type: BrushTypes.PAINT_BRUSH, opacity: 1, thickness: .5 }, [ ToolTypes.CLONE ] : { size: 10, type: BrushTypes.PAINT_BRUSH, opacity: 1, thickness: .5, sourceLayerId: TOOL_SRC_MERGED, coords: { x: 10, y: 15 } }, [ ToolTypes.SELECTION ] : { lockRatio: false, xRatio: 1, yRatio: 1 }, - [ ToolTypes.FILL ] : { smartFill: true }, + [ ToolTypes.FILL ] : { smartFill: true, feather: 5, threshold: 0 }, [ ToolTypes.WAND ] : { threshold: 15, sampleMerged: true }, };