Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Drawing AWT BufferedImage on SWT Canvas

Tags:

java

awt

swt

I am trying to write a SWT component, that is able to take and draw an instance of java.awt.BufferedImage. My problem is that SWT's Image and AWT's BufferedImage are incompatible: SWT components can't draw java.awt.Image, AWT/Swing components can't draw org.eclipse.swt.graphics.Image.

There are several approaches that try to solve this problem in other ways (which also may have some variations, but basically there are these two):

  1. Convert between SWT Image and AWT BufferedImage
  2. Swing/SWT Integration

They all have shortcomings and didn't satisfy my expectations:

  1. The first approach, to convert an SWT Image to a BufferedImage, results in poor performance for large images due to the creation of a new RGB instance for every Pixel.
  2. The second approach has several shortcomings in usability. See the "workarounds" at the end of the linked article.

This lead to the conclusion that I'd try my best to write a component (based on org.eclipse.swt.widgets.Canvas or org.eclipse.swt.widgets.Composite) which allows to draw a BufferedImage directly without any conversion of images.

My approach was to draw it pixel by pixel. Therefore I simply had to get an instance of GC, walk the source BufferedImage line by line, left-to-right and drawing the corresponding Color using GC.setForeground(Color color) and GC.drawPoint(int x, int y).

First, I created a new instance of Color for every pixel, which uses quite a lot of memory and adds an additional delay, since new Color acquires system resources and creating a new object for every pixel also takes its time.

Then I tried to pre-load all possible (24 bit) Colors into an array before drawing the image. This lead to an explosion of memory usage (>= 600 MB), which was clear before I was trying it, but I had to verify it.

Caching only the used Colors also lead to more memory consumption than would have been required.

I think there has to be a more low-level approach that doesn't require that much memory, since SWT is able to draw whole (SWT) Images without consuming that much memory.

I would appreciate any ideas or solutions.

like image 410
pvorb Avatar asked Jun 16 '14 09:06

pvorb


1 Answers

I found out there's a way to "convert" an BufferedImage to an Image by using the original image's data buffer if it is 24 bit RGB. This is possible, since the image formats are compatible.

final BufferedImage original = ImageIO.read(new File("some-image.jpg");

final PaletteData palette =
        new PaletteData(0x0000FF, 0x00FF00, 0xFF0000);

// the last argument contains the byte[] with the image data
final ImageData data = new ImageData(original.getWidth(), original.getHeight(),
        24, palette, 4,
        ((DataBufferByte) original.getData().getDataBuffer()).getData());

final Image converted = new Image(getDevice(), data);

This way, one doesn't have to create thousands of new objects. This approach comes with the disadvantage that one needs to ensure that the original image is of type RGB 24 bit. Otherwise the image has to be converted to this format.

After that, an image can be drawn with the following code:

// get the GC of your component
gc.drawImage(image, 0, 0);

Probably other bit depths can be converted in a similar way, but this is all I need for the moment.

like image 79
pvorb Avatar answered Oct 27 '22 00:10

pvorb