Using threads to paint panel in java

后端 未结 2 1837
孤独总比滥情好
孤独总比滥情好 2021-01-15 14:49

I am writing a program that has a number of different views. One of which is fairly graphics intensive (it displays an interconnected graph). Others just display small but c

相关标签:
2条回答
  • 2021-01-15 15:14

    You cannot let any thread but the Event Dispatch Thread (EDT) touch the GUI. Letting other threads mess with the GUI causes trouble and Exceptions. You can employ a multi-threaded multi-buffering technique. It involves two steps:

    1. In order to parallelize complicated drawing routines, you can simply divide the entire "view" into patches and let one thread draw one patch into one image. Here is a tutorial on working with images in Java.

    2. Once you have the images, you can override paintComponent and use the Graphics.drawImage method to let the EDT display the full or a partial view, by stitching the images of the corresponding patches together.

    In order to avoid unnecessary work, make sure to perform the first step initially and then only after the view changed, else just draw previously computed results again. Furthermore, try to only update part of the patches, if you can narrow down the regions inside the views that have changed between frames.

    Let us assume that your view is at least as many pixels high as your optimal number of threads, so we can just partition the view vertically. Also, let us assume that drawing any pixel takes about the same amount of work as any other, so we can use the same size for every patch. These two assumptions make things a lot easier.

    Code follows. If you don't need your computer to do anything else, you can set nThreads to your number of cores. Note that the code also uses pseudocode for "parallel for" which is explained 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, ...);
        }
    }
    
    0 讨论(0)
  • 2021-01-15 15:29

    I think what you want to do is paint to a buffer in a non UI thread (so one you manage) and just copy the buffer across when done (in the UI thread).

    0 讨论(0)
提交回复
热议问题