You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在JavaFX中实现带羽化硬度调节功能的柔边画笔

JavaFX实现柔边羽化画笔的方案

JavaFX 原生没有直接提供画笔硬度/羽化的封装API,但可以通过以下两种成熟方案实现需求:

方案1:径向渐变自定义笔刷(推荐,效果最接近Photoshop)

核心逻辑是用RadialGradient生成从中心实色到边缘透明的渐变圆形作为笔刷,硬度参数控制透明过渡的区间:硬度1时过渡区间为0(硬边),硬度0时过渡从中心就开始(最大柔边)。

代码修改步骤:

  1. 新增硬度相关变量和控件绑定
// 新增成员变量
private double brushHardness = 0.5; // 取值范围0~1,默认中等硬度
@FXML
private Slider hardnessSlider; // 可在FXML里加个滑块控制硬度

public void initialize() {
    // 原有初始化逻辑外新增绑定
    hardnessSlider.valueProperty().addListener((obs, oldVal, newVal) -> {
        brushHardness = newVal.doubleValue();
    });
    GraphicsContext gc = canvas.getGraphicsContext2D();
    // 鼠标事件替换原有绘制逻辑
    canvas.setOnMouseReleased(e -> last = null);
    canvas.setOnMouseClicked(e -> {
        if (!eraser) drawBrushPoint(e.getX(), e.getY());
    });
    canvas.setOnMouseDragged(e -> {
        double size = Double.parseDouble(brushSize.getText());
        Point2D current = new Point2D(e.getX(), e.getY());
        if (eraser) {
            // 橡皮擦柔边逻辑
            gc.setGlobalBlendMode(BlendMode.CLEAR);
            double stopPos = Math.max(0, Math.min(1, brushHardness));
            RadialGradient eraseGradient = new RadialGradient(
                0, 0, 0.5, 0.5, 0.5, true, CycleMethod.NO_CYCLE,
                new Stop(stopPos, Color.TRANSPARENT),
                new Stop(1, Color.TRANSPARENT)
            );
            gc.setFill(eraseGradient);
            gc.fillOval(e.getX() - size/2, e.getY() - size/2, size, size);
            gc.setGlobalBlendMode(BlendMode.SRC_OVER);
            last = current;
            return;
        }
        if (last != null) {
            // 两点之间补画笔刷,避免线条断档
            double distance = current.distance(last);
            double step = size / 4; // 步长设为笔刷大小的1/4保证平滑
            for (double i = 0; i < distance; i += step) {
                double ratio = i / distance;
                double x = last.getX() + (current.getX() - last.getX()) * ratio;
                double y = last.getY() + (current.getY() - last.getY()) * ratio;
                drawBrushPoint(x, y);
            }
        } else {
            drawBrushPoint(current.getX(), current.getY());
        }
        last = current;
    });
}

// 新增绘制单点球边笔刷的方法
private void drawBrushPoint(double x, double y) {
    double size = Double.parseDouble(brushSize.getText());
    Color brushColor = colorPicker.getValue();
    // 根据硬度计算渐变停止位
    double stopPos = Math.max(0, Math.min(1, brushHardness));
    RadialGradient gradient = new RadialGradient(
        0, 0, 0.5, 0.5, 0.5, true, CycleMethod.NO_CYCLE,
        new Stop(stopPos, brushColor),
        new Stop(1, Color.TRANSPARENT)
    );
    gc.setFill(gradient);
    gc.fillOval(x - size/2, y - size/2, size, size);
}

方案2:高斯模糊简化实现

如果不需要精确的硬度控制,可以通过调整GaussianBlur效果的半径模拟柔边:硬度越低,模糊半径越大。这种方案实现更简单,但性能比自定义渐变笔刷稍差。
示例核心代码:

GaussianBlur blur = new GaussianBlur();
// 硬度0对应模糊半径为笔刷大小的1/2,硬度1对应模糊半径0
blur.setRadius((1 - brushHardness) * Double.parseDouble(brushSize.getText()) / 2);
gc.setEffect(blur);
// 执行原有绘制逻辑
gc.strokeLine(last.getX(), last.getY(), mouseX, mouseY);
gc.setEffect(null); // 绘制完成后清除效果避免影响后续操作

性能优化建议

如果需要频繁切换笔刷大小、硬度,可以提前把常用参数的笔刷预生成Image缓存,绘制时直接调用gc.drawImage(),避免每次实时生成渐变,提升绘制流畅度。

内容的提问来源于stack exchange,提问作者Dániel Sáfár

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.24 20:36:03