Как правильно многопоточность рисовать программу на Java - PullRequest
0 голосов
/ 23 мая 2019

Я работаю над проектом, который я начал из любопытства.Цель моего проекта - использовать Java для создания пакета ресурсов в Minecraft.В конце концов, я хотел добавить собственные песни в Minecraft.Поскольку я довольно новичок в Java и в настоящее время являюсь старшекурсником, я подумал, что это может быть хорошей идеей для изучения Java и изучения.В любом случае, я подумал, что попробую создать свою собственную программу рисования для создания изображения пакета.Вот проблема, с которой я сталкиваюсь сейчас:

В некоторых программах рисования я моделирую функцию инструмента-ведра.Я решил использовать многопоточность, потому что в зависимости от размера области, которую я хочу нарисовать, это может быть долгой задачей.В частности, у меня есть проблемы с перекрывающимися потоками.Раньше я никогда не занимался многопоточностью, но мне кажется, что я настолько близок к правильному пониманию.

Я пытался использовать классы SwingWorker и Thread.Я обнаружил, что они оба достигли одной и той же проблемы.Поэтому я решил избавиться от SwingWorker и использовать Thread, потому что его легче кодировать. Методы рисования paintImmediately (0, 0, getWidth (), getHeight ()) и repaint () очень важны для этой работы. Метод paintImmediately находится внутри pixel [p] .setColor.() метод внутри класса Pixel, который я написал, и я не думаю, что это важно знать для этого вопроса.Существует большая разница в реализации и не реализации метода repaint () - repaint устраняет графические проблемы, но заставляет каждый поток выполняться намного медленнее.Без метода перерисовки потоки идут намного быстрее, но они перекрываются.

Кстати, чтобы мне было легче отслеживать и отлаживать свою программу рисования, я пометил все классы, которые я написал закрытыми внутримой основной класс.Я планирую поместить классы в отдельные файлы 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
        }
    }
}

Bucket:

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;
    }
}

Простое удаление методов перерисовки (и paintImmediately) вызывает очень очевидные последствия:

  • Удаление метода paintImmediately в классе Pixel (здесь не показано) приводит кне показывает перекрытие, но замедляет скорость выполнения каждого потока.

  • Метод paintImmediately показывает результаты мгновенно, но вы можете видеть, что некоторые пиксели имеют перекрывающиеся цвета из другого потока.

  • Добавление метода перерисовки значительно замедляет скорость выполнения каждого потока, но также уменьшает визуальное перекрытие графики.
  • Удаление метода перерисовки возвращает программу к проблемам с перекрытием.

Редактирование: Использование SwingWorker - я заметил, что SwingWorker выдает точно такую ​​же проблему, которая была у меня, когда я использовалПоток класса

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;
    }
}
...