如何创建一个JavaFX节点B,该节点B与另一个节点a具有完全相同的图形表示形式,而不会占用双倍的内存?

如何创建一个JavaFX节点B,该节点B与另一个节点a具有完全相同的图形表示形式,而不会占用双倍的内存?,java,graphics,javafx,Java,Graphics,Javafx,我的团队正在解析一个文件,该文件指定一个图形元素,然后克隆该图形以在许多其他空间位置显示。由于大量使用克隆,一个文件达到了一个标准。我还没有在JavaFX中发现一种机制,可以在场景图中多次复制视觉效果,而每个节点都没有相同的内存占用。这些是矢量图形,不是图像,我们主要使用节点 请帮助找到一种内存高效的方法在屏幕上复制矢量图形,其中每个克隆可能具有其他克隆不共享的任意变换,并防止克隆可能被放大的潜在像素化。我设计了以下问题的简单示例,在我的计算机上,使用Xmx512m第一次运行大约60秒(预热后1

我的团队正在解析一个文件,该文件指定一个图形元素,然后克隆该图形以在许多其他空间位置显示。由于大量使用克隆,一个文件达到了一个标准。我还没有在JavaFX中发现一种机制,可以在场景图中多次复制视觉效果,而每个节点都没有相同的内存占用。这些是矢量图形,不是图像,我们主要使用节点

请帮助找到一种内存高效的方法在屏幕上复制矢量图形,其中每个克隆可能具有其他克隆不共享的任意变换,并防止克隆可能被放大的潜在像素化。我设计了以下问题的简单示例,在我的计算机上,使用Xmx512m第一次运行大约60秒(预热后15秒)后失败。提高Xmx是一个自然的建议,但不是问题的答案

import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.Random;

import javafx.application.Application;
import javafx.application.Platform;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Alert;
import javafx.scene.control.Alert.AlertType;
import javafx.scene.control.Button;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.Pane;
import javafx.scene.shape.LineTo;
import javafx.scene.shape.MoveTo;
import javafx.scene.shape.Path;
import javafx.scene.shape.PathElement;
import javafx.stage.Stage;

/**
 * An example of running out of memory by duplicating a complicated vector graphic.
 */
public class ReplicateVectorGraphic extends Application {

    private Pane drawingBoard;

    @Override
    public void start(Stage primaryStage) {
        drawingBoard = new Pane();
        Button runUntilOOME = new Button("run until OOME");
        runUntilOOME.setOnAction( this::generateGraphics );
        BorderPane root = new BorderPane(drawingBoard, runUntilOOME, null, null, null);
        Scene scene = new Scene(root,400,600);
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    /**
     * Creates new nodes based on the graphics in the simulated (pseudorandom)
     * file until we run out of memory.
     *
     * @param ae
     *            An ignored event that kicked off the operation.
     */
    private void generateGraphics(ActionEvent ae) {
        int n = 0;
        Instant begin = Instant.now();
        try {
            Group completeGraphic = new Group();
            // Hitting 32k clones would be success. (YMMV, but bear in mind
            // arbitrary end-user hardware. also check your Xmx.)
            for (;n <Short.MAX_VALUE; n++) {
                Path p = new Path();
                // load data from the simulated file
                populatePath(p);
                completeGraphic.getChildren().add( p );
            }
            drawingBoard.getChildren().add( completeGraphic );
            new Alert(AlertType.INFORMATION, "32k graphics displayed!").show();
        } catch (OutOfMemoryError oome) {
            final int nbr = n;
            Platform.runLater( () -> displayAlert(nbr, ChronoUnit.SECONDS.between( begin, Instant.now())) );
        }
    }

    private static void displayAlert(long nbr, long seconds) {
        String m = "Cloned graphic "+nbr+" times in "+ seconds +" seconds.";
        Alert a = new Alert(AlertType.ERROR, m);
        a.setTitle( "OOME Alert" );
        a.show();
    }

    private static void populatePath(Path p) {
        // in lieu of a real graphic to load, we'll generate a
        // pseudorandom sequence of points that will be the same each time
        Random r = new Random(42);
        ObservableList<PathElement> pathElements = p.getElements();
        pathElements.add( new MoveTo(r.nextDouble(), r.nextDouble()) );
        // 2048 elements is a reasonable amount of detail for a vector graphic.
        // reducing the detail of the graphic is not an option. we have to load what we're given.
        for (int i=0; i<2048; i++) {
            pathElements.add( new LineTo(r.nextDouble(), r.nextDouble()) );
        }
    }

    public static void main(String[] args) {
        launch(args);
    }
}
导入java.time.Instant;
导入java.time.temporal.ChronoUnit;
导入java.util.Random;
导入javafx.application.application;
导入javafx.application.Platform;
导入javafx.collections.ObservableList;
导入javafx.event.ActionEvent;
导入javafx.scene.Group;
导入javafx.scene.scene;
导入javafx.scene.control.Alert;
导入javafx.scene.control.Alert.AlertType;
导入javafx.scene.control.Button;
导入javafx.scene.layout.BorderPane;
导入javafx.scene.layout.Pane;
导入javafx.scene.shape.LineTo;
导入javafx.scene.shape.MoveTo;
导入javafx.scene.shape.Path;
导入javafx.scene.shape.PathElement;
导入javafx.stage.stage;
/**
*通过复制复杂的矢量图形来耗尽内存的示例。
*/
公共类ReplicateVectorGraphic扩展了应用程序{
私家窗格玻璃拉丝板;
@凌驾
公共无效开始(阶段primaryStage){
drawingBoard=新窗格();
按钮rununtilome=新按钮(“运行到OOME”);
setOnAction(this::generateGraphics);
BorderPane根=新的BorderPane(drawingBoard、RunUntilome、null、null、null);
场景=新场景(根,400600);
初级阶段。场景(场景);
primaryStage.show();
}
/**
*基于模拟场景中的图形创建新节点(伪随机)
*直到我们的内存用完。
*
*@param-ae
*启动操作的已忽略事件。
*/
私有无效生成程序(ActionEvent ae){
int n=0;
即时开始=即时。现在();
试一试{
Group completeGraphic=新组();
//点击32k克隆将是成功的。(YMMV,但请记住
//任意终端用户硬件。同时检查您的Xmx。)
用于(;n displayAlert(nbr,ChronoUnit.SECONDS.between(begin,Instant.now()));
}
}
专用静态无效显示警报(长nbr,长秒){
字符串m=“克隆图形”+nbr+”时间单位为“+秒+”秒。“;
警报a=新警报(AlertType.ERROR,m);
a、 setTitle(“OOME警报”);
a、 show();
}
私有静态void populatePath(路径p){
//我们将生成一个
//每次都相同的点的伪随机序列
随机r=新随机(42);
ObservableList pathElements=p.getElements();
add(newmoveto(r.nextDouble(),r.nextDouble());
//2048元素是矢量图形的合理细节量。
//减少图形的细节不是一个选项。我们必须加载给定的内容。

对于(int i=0;i,与节点相比,节点可能只在场景图中出现一次,您可以自由地重复使用路径的路径元素。因此,不需要在那里复制,这将节省一些空间


我也不明白你为什么在代码中增加“n”两次。

路径是否可以转换为图像,或者是否需要表示为路径,因为路径上存在交互?@hotzst如果你有一种方法可以通过转换为图像来实现这一点,并且可以避免像素化(包括上采样的模糊性)在克隆有缩放变换的情况下,我很想看到它!感谢您捕获了额外的n++。我还没有尝试重用路径元素。这可能会有所帮助。每个路径仍然会有一个新的集合,但这可能不够重要。理想情况下,该解决方案可以适用于所有形状和节点,我们的软件可能需要在som上支持这些形状和节点e点。