如何使用Graphics2d.setPaint()在Java中快速绘制大型数据集

时间:2019-06-17 09:11:20

标签: java

我想在图表上绘制大量数据,其范围从1万点到约2000万点。在1万点时,情节以不错的速度(在一秒钟之内)发生,但在2000万点时,情节似乎需要几分钟。

我正在使用Java编写程序,并用另一种语言重写代码,只是为了提高仅在最大数据集下出现的单个绘图的图形绘图速度。

这是我必须忍受的速度,因为由于数据大小,2000万点图本来就需要很长时间?还是我错过了一些优化标记/方法/等?

我的数据位于13000 x 4096的二维数组中,称为数据。 这是从Main.java中的Plot Function外部填充的。

//In Plot.java
public class PlotG extends JPanel
{
    double xscale = 0.0;
    double yscale = 0.0;
    protected void paintComponent(Graphics g)
    {
        super.paintCompnent(g);
        Graphics2D g2 = (Graphics2D)g;
        g2.setRenderingHint(RenderingHint.Key_ANTIALIASING, RenderingHint.VALUE_ANTIALIAS_ON);

        //Scaling
        int sizew = Data.size();
        int sizeh = Data.get(0).size();          
        xscale = (getWidth()*1.0)/(sizew *1.0);
        yscale = (getHeight()*1.0)/(sizeh  *1.0);

        //Set Colour
        g2.setPaint(Color.GREEN);

        //Plot
        for(int j=0; j<sizew; j++)
        {
            for(int k=0;k<sizeh; k++)
            {
                if(Data.get(j).get(k) > MinimumValueToPlot) //I only plot points above the constant value MinimumValueToPlot
                {
                    int x = xscale*j;
                    int y = yscale*k;
                    g2.fillOval(x,y,1,1);
                }
            }
        }
        return;
    }
}


private Plot dataPlot = new Plot()

public PlotStuff(ArrayList<ArrayList<Double>> In)
{
    Data = In;
    InitPLot(getContentPane());
}

private void InitPlot(Container contentPane)
{
    getContentPane().setBackground(Color.GRAY);
    getContentPane().setLayout(new FlowLayout(FlowLayout.LEADING));
    setMinimumSize(new Dimension(1650, 830));
    pack();

    GraphPanel = new JPanel();
    GraphPanel.setBounds(6,11,1470,750);

    GraphPanel.setBorder( BorderFactory.createTitleBorder( BorderFactory.createLineBorder(Color.GREEN, 2),
                    "Title",
                    TitledBorder.DEFAULT_JUSTIFICATION,
                    TitledBorder.DEFAULT_POSITION,
                    new Font("Tahoma", Font.BOLD, 18),
                    Color.WHITE));
    getContentPanel().add(GraphPanel);
    GraphPanel.setLayout(new BorderLayout());

    dataPlot.setBackGround(Color.BLUE);
    dataPlot.setForeGround(Color.WHITE);
    dataPlot.setPreferredSize(new Dimension(1470, 750));
    GraphPanel.add(dataPlot);
    return;
}

//in Main.java
.
.
PlotStuff p = new PlotStuff(Data);
p.redrawGraph();
p.setVisible();
.
.

如果在常量 MinimumValueToPlot 之上的点数达到2000万或更高,是否有提高速度的方法?给定我的最大可能数据集是13k x 4096 = 53,248,000,这是有可能的,但是到目前为止, MinimumValueToPlot 之上的最高经验点数仅为2000万。

我在JPanel声明中做错了什么吗?我已经看到一些讨论说不应使用 setPreferredSize 吗?是这样吗?

谢谢。

2 个答案:

答案 0 :(得分:0)

在绘制组件中绘制类似的点表示每次重新绘制组件时都要绘制许多点 这意味着调整窗口大小或单击按钮是一项昂贵的操作,因为它具有重新绘制每个点。

在此版本中,我制作了一种方法来重做图形,并且paintComponent仅重绘了后备缓冲区。请注意,向后备缓冲区绘画将与完成绘画差不多快,并且它忽略了swing的显示属性。这样一来,您可以使用轮廓分析器,并查看可以快速执行绘画例程。例如,使用上述Max的一些建议。

public class PlotG extends JPanel
{
    BufferedImage buffer;
    double xscale = 0.0;
    double yscale = 0.0;

    public PlotG(){
        buffer = new BufferedImage(1470, 750, BufferedImage.TYPE_INT_ARGB);
    }
    protected void paintComponent(Graphics g)
    {
        super.paintCompnent(g);
        g.drawImage(buffer, 0, 0, this);
    }

    void updateBuffer(){
        Graphics2D g2 = (Graphics2D)backing.getGraphics();
        g2.setRenderingHint(RenderingHint.Key_ANTIALIASING, RenderingHint.VALUE_ANTIALIAS_ON);

        //Scaling
        int sizew = Data.size();
        int sizeh = Data.get(0).size();          
        xscale = (getWidth()*1.0)/(sizew *1.0);
        yscale = (getHeight()*1.0)/(sizeh  *1.0);

        //Set Colour
        g2.setPaint(Color.GREEN);

        //Plot
        for(int j=0; j<sizew; j++)
        {
            for(int k=0;k<sizeh; k++)
            {
                if(Data.get(j).get(k) > MinimumValueToPlot) //I only plot points above the constant value MinimumValueToPlot
                {
                    int x = xscale*j;
                    int y = yscale*k;
                    g2.fillOval(x,y,1,1);
                }
            }
        }
        repaint();
    }

    public Dimension getPreferredSize(){
        return new Dimension(buffer.getWidth(this), buffer.getHeight(this));
    }
}

setPreferredSize的问题不是性能问题,它可能与秋千布局管理器发生冲突。当您拥有一个组件(例如自定义图形面板)时,您确实希望它确定尺寸。我已将其添加到PlotG类中,因此现在它将尝试根据后备缓冲区进行布局。

您必须更新缓冲区,建议您在将面板设置为可见后在主线程上进行更新。这样就不会阻止edt。

答案 1 :(得分:0)

  1. 您声称Data是2d数组,但不是。它是ArrayList中的ArrayLists。而是使用实际的数组。

您会ArrayList<ArrayList<Double>>而不是double[][]

  1. 在循环中,您正在调用fillOval来设置单个像素的颜色。这导致了不必要的巨大开销。您基本上是在告诉您的应用程序计算大小为1x1的椭圆形2000万次的像素!

我建议您创建一个BufferedImage,获取其像素数组,然后直接设置像素值。然后,完成后,将该图像绘制到Graphics2d对象:

BufferedImage img = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_3BYTE_BGR);
Graphics2D imgGraphics2D = img.createGraphics();
imgGraphics2D.setColor(Color.white);
imgGraphics2D.fillRect(0, 0, getWidth(), getHeight());
imgGraphics2D.dispose();

byte[] pixels = ((DataBufferByte) img.getRaster().getDataBuffer()).getData();
for(int j=0; j < sizew; j++)
{
    for(int k=0; k < sizeh; k++)
    {
        if(data[j][k] > minimumValueToPlot)
        {
            int x = (int)(xscale * j);
            int y = (int)(yscale * k);
            int pixelindex = (y * getWidth() + x) * 3;
            pixels[pixelindex + 1] = 255; // set the green byte
        }
    }
}

g2.setComposite(AlphaComposite.Src);
g2.drawImage(img, 0, 0, null);

如我从记忆中所写的那样,请带着一点盐。

(我还随意将Data重命名为data,将MinimumValueToPlot重命名为minimumValueToPlot。Java中的每个约定变量以小写字母开头,以将它们与类区分开来。)