Вы правы, вы никогда не знаете, когда будет выполнена repaint()
панели.Чтобы избежать нежелательных представлений в компоненте, я обработал бы изображение в фоновом потоке.Таким образом, мне было бы все равно (конечно, я бы), сколько времени займет обработка изображения.Наконец, после обработки изображения я хотел бы поделиться им с GUI (обратно в поток EDT).
Стоит отметить, что инструмент для запуска задач в фоновом режиме в Swing - это Swing Worker. Рабочий Swing позволит вам выполнять долгосрочные задачи в фоновом режиме, а затем обновлять графический интерфейс в соответствующем потоке (EDT - поток рассылки событий).
Я создал пример, в котором кадр состоит изИзображение и кнопка «Изображение процесса».
При нажатии кнопки рабочий запускается.Он обрабатывает изображение (в моем случае он обрезает изображение до 90% ) и, наконец, «освежает» вид новым изображением, красиво и легко.
Кроме того, чтобы ответить на ваш вопрос:
Будет ли потокобезопасным вызывать setPixel для исходного экземпляра из пользовательского потока или его необходимо вызывать вКачать очередь событий, чтобы избежать конфликтов с чтением paintComponent?
Вам не нужно беспокоиться о том, какой метод вы собираетесь использовать во время задачи обработки изображения.Просто не обновляйте там компоненты свинга.Обновите их после процесса.
Предварительный просмотр:
![enter image description here](https://i.stack.imgur.com/uzCrs.gif)
Исходный код:
import java.awt.BorderLayout;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.concurrent.ExecutionException;
import javax.imageio.ImageIO;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
public class TestImage extends JFrame {
private Scratch scratch;
private JButton crop;
public TestImage() {
super("Process image");
setDefaultCloseOperation(EXIT_ON_CLOSE);
getContentPane().setLayout(new BorderLayout());
try {
BufferedImage img = loadImage();
scratch = new Scratch(img);
getContentPane().add(scratch, BorderLayout.CENTER);
} catch (IOException e) {
e.printStackTrace();
}
crop = new JButton("Process image");
crop.addActionListener(e -> processImage());
getContentPane().add(crop, BorderLayout.PAGE_END);
setSize(500, 500);
setLocationRelativeTo(null);
}
private void processImage() {
crop.setEnabled(false);
crop.setText("Processing image...");
new ImageProcessorWorker(scratch, () -> {
crop.setEnabled(true);
crop.setText("Process image");
}).execute();
}
private BufferedImage loadImage() throws IOException {
File desktop = new File(System.getProperty("user.home"), "Desktop");
File image = new File(desktop, "img.png");
return ImageIO.read(image);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> new TestImage().setVisible(true));
}
public static class Scratch extends JPanel implements ImageView {
private static final long serialVersionUID = -5546688149216743458L;
private BufferedImage image;
public Scratch(BufferedImage image) {
this.image = image;
}
@Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(image, 0, 0, null);
}
@Override
public BufferedImage getImage() {
return image;
}
@Override
public void setImage(BufferedImage img) {
this.image = img;
repaint(); //repaint the view after image changes
}
}
public static class ImageProcessorWorker extends SwingWorker<BufferedImage, Void> {
private ImageView view;
private Runnable restoreTask;
public ImageProcessorWorker(ImageView v, Runnable restoreViewTask) {
view = v;
restoreTask = restoreViewTask;
}
@Override
protected BufferedImage doInBackground() throws Exception {
BufferedImage image = view.getImage();
image = crop(image, 0.9d);
Thread.sleep(5000); // Assume it takes 5 second to process
return image;
}
/*
* Taken from
* https://stackoverflow.com/questions/50562388/how-to-crop-image-in-java
*/
public BufferedImage crop(BufferedImage image, double amount) throws IOException {
BufferedImage originalImage = image;
int height = originalImage.getHeight();
int width = originalImage.getWidth();
int targetWidth = (int) (width * amount);
int targetHeight = (int) (height * amount);
// Coordinates of the image's middle
int xc = (width - targetWidth) / 2;
int yc = (height - targetHeight) / 2;
// Crop
BufferedImage croppedImage = originalImage.getSubimage(xc, yc, targetWidth, // widht
targetHeight // height
);
return croppedImage;
}
@Override
protected void done() {
try {
BufferedImage processedImage = get();
view.setImage(processedImage);
if (restoreTask != null)
restoreTask.run();
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
super.done();
}
}
public static interface ImageView {
BufferedImage getImage();
void setImage(BufferedImage img);
}
}