我正在尝试编写一个SWT组件,它能够采取并绘制java.awt.BufferedImage
的实例。我的问题是SWT的Image
和AWT的BufferedImage
不兼容:SWT组件不能绘制java.awt.Image
, AWT/Swing组件不能绘制org.eclipse.swt.graphics.Image
。
有几种方法试图用其他方式解决这个问题(也可能有一些变化,但基本上有这两种):
- 转换SWT图像和AWT buffereimage <
- Swing或SWT集成/gh>
他们都有缺点,没有达到我的期望:
- 第一种方法,将SWT
Image
转换为BufferedImage
,由于为每个像素创建一个新的RGB
实例,导致大图像的性能较差。 - 第二种方法在可用性方面有几个缺点。参见链接文章末尾的"变通方法"。
这导致我的结论是,我会尽我最大的努力编写一个组件(基于org.eclipse.swt.widgets.Canvas
或org.eclipse.swt.widgets.Composite
),允许直接绘制BufferedImage
,而无需任何图像转换。
我的方法是逐像素绘制。因此,我只需要获得GC
的实例,从左到右逐行遍历源BufferedImage
,并使用GC.setForeground(Color color)
和GC.drawPoint(int x, int y)
绘制相应的Color
。
首先,我为每个像素创建了一个Color
的新实例,这使用了相当多的内存并增加了额外的延迟,因为new Color
需要获取系统资源,并且为每个像素创建新对象也需要花费时间。
然后我尝试在绘制图像之前将所有可能的(24位)Color
s预加载到数组中。这导致内存使用量激增(>= 600 MB),这在我尝试之前就很清楚了,但我必须验证它。
只缓存已使用的颜色也会导致比所需更多的内存消耗。
我认为必须有一个更低级的方法,不需要那么多内存,因为SWT能够在不消耗那么多内存的情况下绘制整个(SWT) Image
。
我发现有一种方法可以通过使用原始图像的数据缓冲区来"转换"BufferedImage
到Image
,如果它是24位RGB。这是可能的,因为图像格式是兼容的。
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);
这样,就不需要创建成千上万的新对象。这种方法的缺点是需要确保原始图像是RGB 24位类型。否则,图像必须转换为此格式。
之后,可以使用以下代码绘制图像:
// get the GC of your component
gc.drawImage(image, 0, 0);
可能其他位深度可以用类似的方式转换,但这是我目前所需要的。