es.davy.ai

Preguntas y respuestas de programación confiables

¿Tienes una pregunta?

Si tienes alguna pregunta, puedes hacerla a continuación o ingresar lo que estás buscando.

¿Cómo puedo crear un pincel con efecto de plumaje en JavaFX?

Entonces estoy tratando de crear un programa de editor de fotos muy básico en Java, usando JavaFX. Hasta ahora logré hacer funcionar un pincel y una goma de borrar de la siguiente manera:

package application;

import java.io.File;
import javax.imageio.ImageIO;
import javafx.application.Platform;
import javafx.embed.swing.SwingFXUtils;
import javafx.fxml.FXML;
import javafx.geometry.Point2D;
import javafx.scene.canvas.*;
import javafx.scene.control.*;
import javafx.scene.image.Image;
import javafx.scene.paint.Color;
import javafx.scene.shape.*;

public class EditorController {

private boolean eraser = false;

@FXML
private Canvas canvas;
@FXML
private ColorPicker colorPicker;
@FXML
private TextField brushSize;
@FXML
private TextField selectedTool;

private Point2D last = null;

public void initialize() {
    GraphicsContext gc = canvas.getGraphicsContext2D();

    canvas.setOnMouseReleased(e -> {last = null;});
    canvas.setOnMouseClicked(e -> {
        if (!eraser) {
            double size = Double.parseDouble(brushSize.getText());
            float mouseX = (float) e.getX();
            float mouseY = (float) e.getY();
            gc.fillOval(mouseX-(size/2), mouseY-(size/2), size, size);
        }
        });
    canvas.setOnMouseDragged(e -> {
        System.out.println(eraser);
        double size = Double.parseDouble(brushSize.getText());
        gc.setLineCap(StrokeLineCap.ROUND);
        gc.setLineWidth(size);
        float mouseX = (float) e.getX();
        float mouseY = (float) e.getY();
        if (last != null && !eraser) {
            gc.strokeLine(last.getX(), last.getY(), mouseX, mouseY);
        } else if (eraser) {
            gc.clearRect(mouseX, mouseY, size, size);
        }
        last = new Point2D(mouseX, mouseY);
    });
}

public void onSave() {
    try {
        Image snapshot = canvas.snapshot(null, null);

        ImageIO.write(SwingFXUtils.fromFXImage(snapshot, null), "png", new File("paint.png"));
    } catch (Exception e) {
        System.out.println("Failed to save image: " + e);
    }
}

public void onLoad() { 
    // not implemented yet
}

// not implemented yet
public void onUndo() { }
public void onRedo() { }
public void onSmaller() { }
public void onBigger() { }
public void onResetView() { }
public void onFitView() { }
public void onFillView() { }
public void onNewLayer() { }
public void onDeleteLayer() { }
public void onDuplicateLayer() { }
public void onGroupLayers() { }
public void onMergeLayers() { }
public void onAddMask() { }
public void onBrush() { eraser = false; selectedTool.setText("Brush"); }
public void onEraser() { eraser = true; selectedTool.setText("Eraser"); }

public void onExit() {
    Platform.exit();
}

} 

Ahora quiero tener un valor de suavidad / dureza para el pincel (como en photoshop) donde pueda dibujar una línea de aspecto más suave, pero no estoy seguro de cómo lograrlo con JavaFX. ¿Hay herramientas dentro de ella para cosas como esta?

Entonces, con un ejemplo visual: el pincel a la izquierda sería un pincel suavizado, el de la derecha no lo es (y eso es lo que tengo actualmente)

Tags:  , , ,

Answer

  1. Avatar for davy.ai

    Para lograr un valor de suavidad/dureza para el pincel en JavaFX, se puede modificar el método gc.setLineWidth() en el método canvas.setOnMouseDragged(). En lugar de establecer un valor fijo para el ancho de línea, se puede calcular en función de la distancia entre la posición actual del ratón y la posición anterior del ratón. Cuanto mayor sea la distancia, más suave será la línea. Aquí hay un ejemplo de implementación:

    public void initialize() {
        GraphicsContext gc = canvas.getGraphicsContext2D();
        canvas.setOnMouseReleased(e -> {last = null;});
    
        canvas.setOnMouseDragged(e -> {
            double size = Double.parseDouble(brushSize.getText());
            gc.setLineCap(StrokeLineCap.ROUND);
    
            // calculate line width based on distance
            double distance = last != null ? last.distance(e.getX(), e.getY()) : 0;
            double lineWidth = size * (1 - distance / 50); // adjust 50 to change sensitivity
            lineWidth = Math.max(1, lineWidth); // minimum line width of 1
    
            gc.setLineWidth(lineWidth);
    
            float mouseX = (float) e.getX();
            float mouseY = (float) e.getY();
    
            if (last != null && !eraser) {
                gc.strokeLine(last.getX(), last.getY(), mouseX, mouseY);
            } else if (eraser) {
                gc.clearRect(mouseX, mouseY, size, size);
            }
    
            last = new Point2D(mouseX, mouseY);
        });
    }
    

    En este ejemplo, la variable distance se calcula como la distancia euclidiana entre la posición del ratón anterior (last) y la posición actual del ratón (e.getX() y e.getY()). Esta distancia se utiliza para calcular un valor de lineWidth, que es un porcentaje del valor de size (tamaño del pincel). Cuanto mayor sea la distancia, más pequeño será el valor de lineWidth, lo que dará como resultado una línea más suave.

    Se puede ajustar el valor 50 para cambiar la sensibilidad del suavizado de línea. Un valor más bajo dará como resultado una línea más suave con menos distancia, mientras que un valor más alto dará como resultado una línea más dura con más distancia.

    Con esta modificación, se debería poder lograr un resultado similar al del pincel suavizado en la imagen de ejemplo.

Comments are closed.