Можно ли добавить заливку LinearGradient в SVGPath, который уже имеет заливку ImagePattern? - PullRequest
2 голосов
/ 09 апреля 2019

В моем основном приложении у меня есть несколько SVGPath, которые я добавляю в XYChart. Иногда они имеют заливку ImagePattern, которая теперь должна иметь заливку LinearGradient. Заливка ImagePattern является перекрестной штриховкой, и ее необходимо закрасить с помощью LinearGradient так же, как если бы это был сплошной прямоугольник с примененным LinearGradient. SVGPath также имеет пунктирный контур, и LinearGradient должен заполнять пунктирный контур и заливку ImagePattern, так как они являются частью одной и той же формы.

Я написал пример кода, чтобы показать, где я нахожусь. Это окрашивает перекрестную штриховку, поскольку это создано и выглядит хорошо, но не эффект, который я описываю выше, поскольку каждый крест в ImagePattern имеет LinearGradient, примененный индивидуально. В идеале LinearGradient должен быть применен к окончательному SVGPath после применения заливки ImagePattern. Я также попробовал некоторые эффекты, используя Blend и ColorInput, но не смог приблизиться к решению.

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.SnapshotParameters;
import javafx.scene.image.Image;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.paint.CycleMethod;
import javafx.scene.paint.ImagePattern;
import javafx.scene.paint.LinearGradient;
import javafx.scene.paint.Paint;
import javafx.scene.paint.Stop;
import javafx.scene.shape.Line;
import javafx.scene.shape.SVGPath;
import javafx.stage.Stage;


public class Main extends Application {
    @Override
    public void start(Stage primaryStage) {
        try {
            List<Color> colors = Arrays.asList(Color.RED, Color.YELLOW, Color.GREEN);
            ArrayList<Stop> stops = new ArrayList<>(colors.size() * 2);

            for (int i = 0; i < colors.size(); i++) {
                stops.add(new Stop(getOffset(i, colors.size()), colors.get(i)));
                stops.add(new Stop(getOffset(i + 1, colors.size()), colors.get(i)));
            }

            LinearGradient lg = new LinearGradient(0, 0, 20, 20, false, CycleMethod.REPEAT, stops);

            SVGPath svgPath = new SVGPath();
            svgPath.setContent("M-84.1487,-15.8513 a22.4171,22.4171 0 1 0 0,31.7026 h168.2974 a22.4171,22.4171 0 1 0 0,-31.7026 Z");

            Image hatch = createCrossHatch(lg);
            ImagePattern pattern = new ImagePattern(hatch, 0, 0, 10, 10, false);

            svgPath.setFill(pattern);
            svgPath.setStroke(lg);

            BorderPane root = new BorderPane();
            root.setCenter(svgPath);
            Scene scene = new Scene(root, 400, 400);
            primaryStage.setScene(scene);
            primaryStage.show();
        }
        catch (Exception e) {
            e.printStackTrace();
        }
    }

    protected static Image createCrossHatch(Paint paint) {
        Pane pane = new Pane();
        pane.setPrefSize(20, 20);
        pane.setStyle("-fx-background-color: transparent;");
        Line fw = new Line(-5, -5, 25, 25);
        Line bw = new Line(-5, 25, 25, -5);
        fw.setStroke(paint);
        bw.setStroke(paint);
        fw.setStrokeWidth(3);
        bw.setStrokeWidth(3);
        pane.getChildren().addAll(fw, bw);
        new Scene(pane);
        SnapshotParameters sp = new SnapshotParameters();
        sp.setFill(Color.TRANSPARENT);

        return pane.snapshot(sp, null);
    }

    private double getOffset(double i, int count) {
        return (((double) 1) / (double) count * (double) i);
    }

    public static void main(String[] args) {
        launch(args);
    }
}

Если вы запустите предоставленный код, то увидите, что он рисует собачью кость. Цвета линейного градиента пунктирного контура должны продолжаться через заливку перекрестного штриховки ImagePattern. Я знаю, почему заштрихованный ImagePattern окрашен так, как он есть, но это лучший компромисс, который у меня есть в настоящее время. Как уже упоминалось, я хотел бы иметь возможность применять заливку LinearGradient ко всей фигуре после применения заливки ImagePattern, чтобы LinearGradient влиял на всю фигуру одинаково.

Спасибо

1 Ответ

0 голосов
/ 18 апреля 2019

Прямой способ применения отсутствует и объединяет две краски на одном узле.Мы можем наложить много разных красок (например, сплошные, линейные градиенты или даже рисунки), используя цвет фона через css, но это не скомбинирует.

Итак, чтобы объединить две разные краскис одной стороны - линейный градиент, с другой - заливка шаблона, нам нужно применить их к двум узлам и использовать эффект смешивания между обеими красками.

Согласно опубликованному коду, это SVGPath слинейный градиент:

@Override
public void start(Stage primaryStage) {
    Node base = getNodeWithGradient();

    BorderPane root = new BorderPane();
    Group group = new Group(base);
    root.setCenter(group);

    Scene scene = new Scene(root, 400, 200);
    primaryStage.setScene(scene);
    primaryStage.show();
}

private SVGPath getNodeWithGradient() {
    List<Color> colors = Arrays.asList(Color.RED, Color.YELLOW, Color.GREEN);
    ArrayList<Stop> stops = new ArrayList<>(colors.size() * 2);

    for (int i = 0; i < colors.size(); i++) {
        stops.add(new Stop(getOffset(i, colors.size()), colors.get(i)));
        stops.add(new Stop(getOffset(i + 1, colors.size()), colors.get(i)));
    }

    LinearGradient lg = new LinearGradient(0, 0, 20, 20, false, CycleMethod.REPEAT, stops);

    SVGPath svgPath = getSVGPath();
    svgPath.setFill(lg);
    svgPath.setStroke(lg);

    return svgPath;
}

private SVGPath getSVGPath() {
    SVGPath svgPath = new SVGPath();
    svgPath.setContent("M-84.1487,-15.8513 a22.4171,22.4171 0 1 0 0,31.7026 h168.2974 a22.4171,22.4171 0 1 0 0,-31.7026 Z");
    return svgPath;
}

private double getOffset(double i, int count) {
    return (((double) 1) / (double) count * (double) i);
}

Linear Gradient

Пока это SVGPath с заливкой рисунка:

@Override
public void start(Stage primaryStage) {
    Node overlay = getNodeWithPattern();

    BorderPane root = new BorderPane();
    Group group = new Group(overlay);

    root.setCenter(group);
    Scene scene = new Scene(root, 400, 200);
    primaryStage.setScene(scene);
    primaryStage.show();
}

private SVGPath getNodeWithPattern() {
    Image hatch = createCrossHatch();
    ImagePattern pattern = new ImagePattern(hatch, 0, 0, 10, 10, false);

    SVGPath svgPath = getSVGPath();
    svgPath.setFill(pattern);

    return svgPath;
}

private SVGPath getSVGPath() {
    SVGPath svgPath = new SVGPath();
    svgPath.setContent("M-84.1487,-15.8513 a22.4171,22.4171 0 1 0 0,31.7026 h168.2974 a22.4171,22.4171 0 1 0 0,-31.7026 Z");
    return svgPath;
}

private static Image createCrossHatch() {
    Pane pane = new Pane();
    pane.setPrefSize(20, 20);
    Line fw = new Line(-5, -5, 25, 25);
    Line bw = new Line(-5, 25, 25, -5);
    fw.setStroke(Color.BLACK);
    bw.setStroke(Color.BLACK);
    fw.setStrokeWidth(3);
    bw.setStrokeWidth(3);
    pane.getChildren().addAll(fw, bw);
    new Scene(pane);
    SnapshotParameters sp = new SnapshotParameters();
    return pane.snapshot(sp, null);
}

Image Pattern

Теперь хитрость заключается в том, чтобы объединить оба узла SVGPath, добавив режим смешивания к верхнему.

Согласно JavaDoc для BlendMode.ADD:

Компоненты цвета и альфа-канала из верхнего ввода добавляются к компонентам из нижнего ввода.

@Override
public void start(Stage primaryStage) {
    Node base = getNodeWithGradient();
    Node overlay = getNodeWithPattern();
    overlay.setBlendMode(BlendMode.ADD);

    BorderPane root = new BorderPane();
    Group group = new Group(base, overlay);

    root.setCenter(group);
    Scene scene = new Scene(root, 400, 200);
    primaryStage.setScene(scene);
    primaryStage.show();
}

private SVGPath getNodeWithGradient() {
    List<Color> colors = Arrays.asList(Color.RED, Color.YELLOW, Color.GREEN);
    ArrayList<Stop> stops = new ArrayList<>(colors.size() * 2);

    for (int i = 0; i < colors.size(); i++) {
        stops.add(new Stop(getOffset(i, colors.size()), colors.get(i)));
        stops.add(new Stop(getOffset(i + 1, colors.size()), colors.get(i)));
    }

    LinearGradient lg = new LinearGradient(0, 0, 20, 20, false, CycleMethod.REPEAT, stops);

    SVGPath svgPath = getSVGPath();
    svgPath.setFill(lg);
    svgPath.setStroke(lg);
    return svgPath;
}

private SVGPath getNodeWithPattern() {
    Image hatch = createCrossHatch();
    ImagePattern pattern = new ImagePattern(hatch, 0, 0, 10, 10, false);

    SVGPath svgPath = getSVGPath();
    svgPath.setFill(pattern);
    return svgPath;
}

private SVGPath getSVGPath() {
    SVGPath svgPath = new SVGPath();
    svgPath.setContent("M-84.1487,-15.8513 a22.4171,22.4171 0 1 0 0,31.7026 h168.2974 a22.4171,22.4171 0 1 0 0,-31.7026 Z");
    return svgPath;
}

private static Image createCrossHatch() {
    Pane pane = new Pane();
    pane.setPrefSize(20, 20);
    Line fw = new Line(-5, -5, 25, 25);
    Line bw = new Line(-5, 25, 25, -5);
    fw.setStroke(Color.BLACK);
    bw.setStroke(Color.BLACK);
    fw.setStrokeWidth(3);
    bw.setStrokeWidth(3);
    pane.getChildren().addAll(fw, bw);
    new Scene(pane);
    SnapshotParameters sp = new SnapshotParameters();
    return pane.snapshot(sp, null);
}

private double getOffset(double i, int count) {
    return (((double) 1) / (double) count * (double) i);
}

И мы получаем желаемый результат:

Linear Gradient + Image Pattern

...