如何在Java中正确对绘画程序进行多线程处理?

时间:2019-05-22 23:49:11

标签: java multithreading swing paint java.util.concurrent

我正在出于好奇而开始一个项目。我项目的目的是使用Java在Minecraft中创建资源包。最后,我想向Minecraft添加自定义歌曲。由于我是Java的新手,现在是本科生,所以我认为探索Java和学习可能是个好主意。无论如何,我想我会尝试创建自己的自定义绘画程序来创建包装图像。这是我现在遇到的问题:

我正在为某些绘画程序中的存储桶工具功能建模。我决定对其进行多线程处理,因为根据要绘制的区域大小,这可能是一个漫长的任务。具体来说,我在重叠线程方面遇到问题。我以前从未做过多线程处理,但是我感觉自己已经很接近正确了。

我尝试使用SwingWorker和Thread类。我最终发现它们都达到了完全相同的问题。因此,我决定摆脱SwingWorker并使用Thread,因为它更易于编码。 paintImmediately(0,0,getWidth(),getHeight())和repaint()方法对于完成这项工作至关重要。 paintImmediately方法位于 pixel [p]内部。我编写的Pixel类中的setColor()方法,对于这个问题,我认为并不重要。实现和不实现repaint()方法有很大的不同- repaint修复了图形问题,但导致每个线程执行得慢得多。如果没有重绘方法,线程的运行速度会大大提高,但它们会重叠。

顺便说一句,为了使我更容易监视和调试我的绘画程序,我已经将自己编写的所有类标记为私有。我计划一旦Paint程序按照我想要的方式运行,就将这些类放入单独的Java文件中。

PixelBoard:

private class PixelBoard extends JPanel implements MouseListener, MouseMotionListener {
    private boolean leftMouse = false;
    private HiddenOptions hiddenOptions;

    @Override
    public void mousePressed(MouseEvent m) {
        leftMouse = (hiddenOptions == null) ? m.getButton() == MouseEvent.BUTTON1 : false;
        boolean middleMouse = m.getButton() == MouseEvent.BUTTON2, rightMouse = (hiddenOptions == null) ? m.getButton() == MouseEvent.BUTTON3 : false;
        Pixel p = (Pixel) m.getSource();
        if (leftMouse) {
            if (menuBar.isBucket()) {
                ArrayList<Integer> bucket = new Bucket(p.getIndex(), p.getColor()).bucket();
                drawnPixels.addAll(bucket);
            } else {
                setColor(p, colors.getColor());
            }
        } else if (middleMouse) {
            if (hiddenOptions == null)
                hiddenOptions = new HiddenOptions();
        } else if (rightMouse) {
            colors.setColor(p.getColor()); //'colors' is a JColorChooser declared in the Main class
        }
    }
}

存储桶:

private class Bucket implements Runnable {
    private ArrayList<Integer> bucket = new ArrayList<Integer>(), getPixels = new ArrayList<Integer>();
    private Color getColor = colors.getColor(), groupColor;

    public Bucket(int startPixel, Color groupColor) {
        this.groupColor = groupColor;
        if (groupColor != getColor) {
            add(startPixel);
            getPixels.add(startPixel);
        }
        new Thread(this).start();
    }

    @Override
    public void run() {
        while (!getPixels.isEmpty()) {
            move();
        }
    }

    public ArrayList<Integer> bucket() {
        return bucket;
    }

    private void add(int p) {
        pixel[p].setColor(getColor);
        pixel[p].setToolTipText(pixel(p));
        container.repaint();
        bucket.add(p);
    }

    private void move() {
        ArrayList<Integer> pix = new ArrayList<Integer>();
        for (int p : getPixels) {
            for (int m : move(p)) {
                if (!bucket.contains(m)) {
                    if (pixel[m].getColor().equals(groupColor)) {
                        add(m);
                        pix.add(m);
                    }
                }
            }
        }
        getPixels = pix;
    }

    private boolean[] pixelLocations(int index) {
        boolean topLeftCorner = index == 0, topRightCorner = index == pixels - 1;
        boolean bottomLeftCorner = index == lastPixel - pixels, bottomRightCorner = index == lastPixel - 1;
        boolean leftEdge = index % pixels == 0, rightEdge = (index + 1) % pixels == 0;
        boolean topEdge = index > 0 && index < pixels - 1, bottomEdge = index > lastPixel - pixels && index < lastPixel - 1;
        return new boolean[] {topLeftCorner, topRightCorner, bottomLeftCorner, bottomRightCorner, leftEdge, rightEdge, topEdge, bottomEdge};
    }

    private int[] move(int p) {
        int[] move = {p + 1, p - 1, p + pixels, p - pixels};
        boolean[] location = pixelLocations(p);
        if (location[0])
            move = move(move, new int[] {0, 2});
        else if (location[1])
            move = move(move, new int[] {1, 2});
        else if (location[2])
            move = move(move, new int[] {0, 3});
        else if (location[3])
            move = move(move, new int[] {1, 3});
        else if (location[4])
            move = move(move, new int[] {0, 2, 3});
        else if (location[5])
            move = move(move, new int[] {1, 2, 3});
        else if (location[6])
            move = move(move, new int[] {0, 1, 2});
        else if (location[7])
            move = move(move, new int[] {0, 1, 3});
        return move;
    }

    private int[] move(int[] move, int[] newMove) {
        int[] m = new int[newMove.length];
        for (int i = 0; i < m.length; i++)
            m[i] = move[newMove[i]];
        return m;
    }
}

简单地(立即删除)重新绘制方法会产生非常明显的影响:

  • 删除Pixel类中的paintImmediately方法(此处未显示) 不显示重叠,但是会降低每个线程的执行速度。

  • paintImmediately方法确实可以立即显示结果,但是您可以 看到有些像素与另一像素有一些重叠的颜色 线。

  • 添加重绘方法会大大降低执行速度 每个线程的速度,但也减少了图形重叠 视觉上。
  • 删除repaint方法会将程序还原到 重叠的问题。

编辑:使用SwingWorker-我注意到SwingWorker出现了与使用Thread类时完全相同的问题

private class Bucket extends SwingWorker<ArrayList<Integer>, Void> {
    private ArrayList<Integer> getBucket, getPixels = new ArrayList<Integer>();
    private Color groupColor;
    private int startPixel;

    public Bucket(int startPixel, Color groupColor) {
        this.startPixel = startPixel; this.groupColor = groupColor;
        execute();
    }

    @Override
    protected ArrayList<Integer> doInBackground() throws Exception {
        ArrayList<Integer> bucket = new ArrayList<Integer>();
        Color getColor = colors.getColor(); 
        if (groupColor != getColor) {
            bucket = add(startPixel, getColor, bucket);
            getPixels.add(startPixel);
        }
        while (!getPixels.isEmpty()) {
            move(getColor, bucket);
        }
        return bucket;
    }

    @Override
    protected void done() {
        try {
            getBucket = get();
        } catch (InterruptedException|ExecutionException e) {
            e.printStackTrace();
        }
    }

    public ArrayList<Integer> getBucket() {
        return getBucket;
    }

    private ArrayList<Integer> add(int p, Color getColor, ArrayList<Integer> bucket) {
        pixel[p].setColor(getColor);
        pixel[p].setToolTipText(pixel(p));
        bucket.add(p);
        return bucket;
    }

    private void move(Color getColor, ArrayList<Integer> bucket) {
        ArrayList<Integer> pix = new ArrayList<Integer>();
        for (int p : getPixels) {
            for (int m : move(p)) {
                if (!bucket.contains(m)) {
                    if (pixel[m].getColor().equals(groupColor)) {
                        bucket = add(m, getColor, bucket);
                        pix.add(m);
                    }
                }
            }
        }
        getPixels = pix;
    }

    private int[] move(int p) {
        int[] move = {p+1, p-1, p+pixels, p-pixels};
        boolean[] location = pixelLocations(p);
        if (location[0])
            move = move(move, new int[] {0, 2});
        else if (location[1])
            move = move(move, new int[] {1, 2});
        else if (location[2])
            move = move(move, new int[] {0, 3});
        else if (location[3])
            move = move(move, new int[] {1, 3});
        else if (location[4])
            move = move(move, new int[] {0, 2, 3});
        else if (location[5])
            move = move(move, new int[] {1, 2, 3});
        else if (location[6])
            move = move(move, new int[] {0, 1, 2});
        else if (location[7])
            move = move(move, new int[] {0, 1, 3});
        return move;
    }

    private int[] move(int[] move, int[] newMove) {
        int[] m = new int[newMove.length];
        for (int i = 0; i < m.length; i++)
            m[i] = move[newMove[i]];
        return m;
    }
}

0 个答案:

没有答案