feat: add feather brush tool with adjustable settings and blending functionality
- Implemented feather brush tool in the brushRaster module, allowing for feathered edges in brush strokes. - Added new FeatherControls component for UI adjustments of feather settings including size, radius, strength, and smoothing. - Updated brush preview logic to accommodate feather tool alongside existing brush and eraser tools. - Enhanced layer rendering to support feather mask previews and interactions. - Introduced blending logic for feathered strokes to mix blurred mask values with original pixels. - Added unit tests for feather blending functionality and tool keybindings. - Updated cursor handling to reflect feather tool usage.
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import type { ImageDocument } from "@core/document";
|
||||
import type { Vec2D } from "@core/geometry";
|
||||
import type { Layer } from "@core/layer";
|
||||
import { getLayerMask } from "@core/layer-mask-utils";
|
||||
import type { EditorState } from "@editor/state";
|
||||
import type { RgbaColor, WebGlRendererContext } from "./types";
|
||||
|
||||
@@ -78,13 +79,13 @@ function drawPreviewPass(
|
||||
}
|
||||
|
||||
function resolveBrushPreview(document: ImageDocument, editor: EditorState, canvas: HTMLCanvasElement) {
|
||||
if (!editor.brushPreview || (editor.tools.activeTool !== "brush" && editor.tools.activeTool !== "eraser")) return undefined;
|
||||
if (!editor.brushPreview || (editor.tools.activeTool !== "brush" && editor.tools.activeTool !== "eraser" && editor.tools.activeTool !== "feather")) return undefined;
|
||||
if (editor.tools.interactionMode.type === "temporary-pan" || (editor.tools.interactionMode.type === "tool" && editor.tools.interactionMode.tool === "pan")) return undefined;
|
||||
|
||||
const layer = resolveBrushTargetLayer(document, editor);
|
||||
if (!layer) return undefined;
|
||||
|
||||
const size = Math.max(1, editor.tools.brush.size);
|
||||
const size = Math.max(1, editor.tools.activeTool === "feather" ? editor.tools.feather.size : editor.tools.brush.size);
|
||||
const zoom = editor.viewport.zoom;
|
||||
return {
|
||||
center: documentPointToScreenPoint(canvas, editor.brushPreview.position, editor),
|
||||
@@ -92,7 +93,7 @@ function resolveBrushPreview(document: ImageDocument, editor: EditorState, canva
|
||||
x: Math.max(1, Math.abs(layer.transform.scale.x) * size * zoom * 0.5),
|
||||
y: Math.max(1, Math.abs(layer.transform.scale.y) * size * zoom * 0.5),
|
||||
},
|
||||
hardness: Math.max(0, Math.min(1, editor.tools.brush.hardness / 100)),
|
||||
hardness: editor.tools.activeTool === "feather" ? 0.72 : Math.max(0, Math.min(1, editor.tools.brush.hardness / 100)),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -101,7 +102,10 @@ function resolveBrushTargetLayer(document: ImageDocument, editor: EditorState):
|
||||
const layerId = editor.maskEdit?.kind === "inpaintRegion" ? editor.maskEdit.targetLayerId : editor.maskEdit?.maskLayerId ?? editor.selection.layerIds[0];
|
||||
if (!layerId) return undefined;
|
||||
|
||||
const layer = findPaintableLayer(document.artboards.flatMap((artboard) => artboard.layers), layerId);
|
||||
const layers = document.artboards.flatMap((artboard) => artboard.layers);
|
||||
const selectedLayer = findPaintableLayer(layers, layerId);
|
||||
const maskLayerId = editor.tools.activeTool === "feather" && !editingMask && selectedLayer ? getLayerMask(selectedLayer)?.maskLayerId : undefined;
|
||||
const layer = maskLayerId ? findPaintableLayer(layers, maskLayerId) : selectedLayer;
|
||||
if (!layer || layer.locked || (!editingMask && !layer.visible)) return undefined;
|
||||
return layer;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user