我正在编写一个包含许多不同视图的程序。其中一个是相当图形密集型(它显示一个互连的图形)。其他人只是展示小而复杂的图表。
我发现主视图的绘制时间很长(甚至只是绘制当前可见的区域),并且在绘制时,界面的其余部分变得非常慢。
我的问题是,我可以创建一个新的线程来处理绘画 - 如果是这样,它会导致性能提升,我怀疑它不会。我尝试过以下方法:
创建一个抽象类ThreadPaintablePanel,我的复杂视图继承自。
public abstract class ThreadPaintablePanel extends JPanel{
private Thread painter;
public abstract void paintThread(Graphics g);
protected void callPaintThread(Graphics g){
if(painter != null){
painter.interrupt();
}
painter = new Thread(new PaintRunnable(this, g));
painter.start();
}
}
然后在我的复杂视图中,我的paintComponent
方法就是:super.callPaintThread(g);
重写的paintThread
方法包含我的所有绘画代码。然而,这会导致未上漆的面板。我错过了一些明显的东西吗?
由于
答案 0 :(得分:8)
除了Event Dispatch Thread(EDT)之外,您不能让任何线程触及GUI。让其他线程弄乱GUI会导致麻烦和异常。您可以使用多线程multi-buffering技术。它涉及两个步骤:
为了并行化复杂的绘图程序,您可以简单地将整个“视图”划分为补丁,让一个线程将一个补丁绘制到一个图像中。 Here is a tutorial on working with images in Java。
获得图像后,您可以覆盖paintComponent
并使用Graphics.drawImage method让EDT显示完整或部分视图,方法是将相应色块的图像拼接在一起。
为了避免不必要的工作,请确保最初执行第一步,然后仅在视图更改后执行,否则只需再次绘制先前计算的结果。此外,如果您可以缩小视图内已在帧之间发生更改的区域,请尝试仅更新部分修补程序。
让我们假设您的视图至少与最佳线程数一样多,因此我们可以垂直分割视图。另外,让我们假设绘制任何像素所需的工作量与其他像素相同,因此我们可以为每个补丁使用相同的大小。这两个假设使事情变得容易多了。
代码如下。如果您不需要计算机执行任何其他操作,则可以set nThreads
to your number of cores。请注意,该代码还使用伪代码“parallel for”,解释为here:
// let multiple threads write all patches
public BufferedImage[] writePatches(...)
{
// Given data:
const int nThreads = ...; // the amount of threads that you want
Rectangle viewBox = ...; // The view rectangle
// Immediate data:
Dimension viewSize = viewBox.getSize();
int defaultPatchHeight = (int)ceil((float)viewSize.height / nThreads);
int lastPatchHeight = viewSize.height - (nThreads-1) * defaultPatchHeight;
// The actual "buffer" is a set of images
BufferedImage[] images = new BufferedImage[nThreads];
// ...
// pseudocode for parallel processing of a for loop
parallel-for (nThreads, threadId)
{
// the starting point and size of this thread's patch
// handle boundary (last) patch
int w = viewBox.width;
int h = threadId == nThread-1 ? lastPatchHeight : defaultPatchHeight;
int x = viewBox.x;
int y = viewBox.y + threadId * defaultPatchHeight;
BufferedImage patch = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
Graphics2D g = off_Image.createGraphics();
// use g to draw to patch image here
// better yet: Re-use existing patches and only update the parts that changed.
images[threadId] = patch;
}
return images;
}
// ...
// override paintComponent
@Override
void paintComponent(Graphics gg)
{
Graphics2D g = (Graphics2D) gg;
// ...
// stitch all images together (you can also just display only some images here)
for (int threadId = 0; threadId < nThreads; ++threadId)
{
int w = viewBox.width;
int h = threadId == nThread-1 ? lastPatchHeight : defaultPatchHeight;
int x = viewBox.x;
int y = viewBox.y + threadId * defaultPatchHeight;
// use pre-computed images here
BufferedImage patch = images[threadId];
g.drawImage(patch, x, y, ...);
}
}
答案 1 :(得分:0)
我认为你想要做的是绘制到非UI线程中的缓冲区(这是你管理的一个),只需在完成后复制缓冲区(在UI线程中)。