I get weird black areas in BufferedImage, how can I fix this?

I am currently writing a Fractal Explorer program, and I am having a very strange problem: I draw a fractal on a BufferedImage and I get random black areas in this image. Screenshots: http://imgur.com/a/WalM7

The image is multi-threaded: the large image is divided into four (since I have a quad-core processor) sub-images, which are calculated individually. Black areas appear at the beginning of each of the sub-images. They are always rectangular, not necessarily following the order in which the pixels are calculated (from left to right, but the area does not always extend to the far side of the sub-image).

I checked that immediately after drawing the pixel (with Graphics.drawLine), BufferedImage.getRGB returns the correct color for the pixel, but after the calculation is completed, it can return black instead, since the pixel is drawn on the screen.

The problem seems to disappear if I turn off multi-threaded computing (assigning only one core javaw.exe via task manager), but I really don't want to give up multi-core computing. Has anyone else run into this problem (I didn't find anything via Google and stackoverflow) and do you know how to fix this?

The call to Graphics.drawLine is synchronized on the Graphics object; if I additionally synchronized it with BufferedImage, nothing will change.

If you want to see the error yourself, you can download the program http://code.lucaswerkmeister.de/jfractalizer/ . It is also available on GitHub (https://github.com/lucaswerkmeister/JFractalizer), but I just recently switched to GitHub, and in the first GitHub this problem is already obvious.

+4
source share
2 answers

I think the problem is that neither BufferedImage nor Graphics are thread safe and you see stale values ​​in the stream that BufferedImage reads after calculation.

BufferedImage sync, as you said, should really help. But keep in mind that you must synchronize all calls from all threads, including read-only access. Therefore, I assume that the stream that draws the BufferedImage on some component (which should be an AWT stream) does this without synchronization and therefore sees stale values.

However, I would suggest that instead of sharing a BufferedImage from multiple threads, you give each thread a separate image onto which it can be drawn. Then, after all the threads have completed, combine their work on the new image in the AWT stream.

In addition, I suggest you use the ExecutorService if you do not. This has the advantage that the visibility problems of the return values ​​of Callable tasks (in your case, portions of the workflow image) are handled by library classes.

If you combine these two approaches, you will not need to perform manual synchronization, which is always good (since it is easy to make a mistake).

+3
source

Buffered images may not be thread safe because their data may be displayed on a graphics card. However, this can be overridden. Using the secret method ((DataBufferInt) image.getRaster().getDataBuffer()).getData() for high-speed full image drawing (the type of data buffer depends on the image type you selected), you will get the image without acceleration. As long as you never write to the same pixel twice, this should be theoretically completely safe. But don't forget to attach () your streams before reading pixels from the image. join () from a thread, which is not actually recommended, since this requires the death of the thread.

Note: The flicker that you observe is probably an artifact of how awt displays the screen. It starts immediately, that is, every action you take immediately refreshes the screen. This slows down the rendering of several objects directly in the window. You can bypass flicker by implementing a double buffering strategy. I like to draw an intermediate image and then draw only that image on the screen.

0
source

Source: https://habr.com/ru/post/1437558/


All Articles