动机:
我的目标是以最有效的方式将 AWT BufferedImage
转换为 SWT ImageData
。这个问题的典型答案是整个图片的逐像素转换,即 O(n^2) 复杂度。如果他们可以按原样交换整个像素矩阵,效率会更高。 BufferedImage
在确定颜色和 alpha 的详细编码方式方面似乎非常灵活。
为了向您提供更广泛的背景,我使用 Apache Batik 编写了一个 SVG 图标按需光栅化器,但它适用于 SWT (Eclipse) 应用程序。 Batik 仅呈现到 java.awt.image.BufferedImage
,但 SWT 组件需要 org.eclipse.swt.graphics.Image
。
它们的支持光栅对象:java.awt.image.Raster
和org.eclipse.swt.graphics.ImageData
代表完全相同的东西,它们只是一个包装器表示像素的字节值的二维数组。如果我可以使用其中之一来使用颜色编码,瞧,我可以按原样重用支持数组。
我已经走了很远了,这有效:
// defined blank "canvas" for Batik Transcoder for SVG to be rasterized there
public BufferedImage createCanvasForBatik(int w, int h) {
new BufferedImage(w, h, BufferedImage.TYPE_4BYTE_ABGR);
}
// convert AWT's BufferedImage to SWT's ImageData to be made into SWT Image later
public ImageData convertToSWT(BufferedImage bufferedImage) {
DataBuffer db = bufferedImage.getData().getDataBuffer();
byte[] matrix = ((DataBufferByte) db).getData();
PaletteData palette =
new PaletteData(0x0000FF, 0x00FF00, 0xFF0000); // BRG model
// the last argument contains the byte[] with the image data
int w = bufferedImage.getWidth();
int h = bufferedImage.getHeight();
ImageData swtimgdata = new ImageData(w, h, 32, palette);
swtimgdata.data = matrix; // ImageData has all field public!!
// ImageData swtimgdata = new ImageData(w, h, 32, palette, 4, matrix); ..also works
return swtimgdata;
}
除了透明度之外,一切都有效:(
看起来ImageData
需要(总是?)alpha是一个单独的栅格,请参阅颜色栅格中的ImageData.alphaData
,请参阅ImageData.data
;两者都是 byte[]
类型。
有没有办法让ImageData
接受ARGB
模型?那是alpha与其他颜色混合吗?我怀疑,所以我走了另一条路。使 BufferedImage 为颜色和 Alpha 使用单独的数组(也称为栅格或“带”)。 ComponentColorModel
和 BandedRaster
似乎正是为了这些事情而设计的。
到目前为止我到达这里:
public BufferedImage createCanvasForBatik(int w, int h) {
ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_sRGB);
int[] nBits = {8, 8, 8, 8}; // ??
ComponentColorModel colorModel = new ComponentColorModel(cs, nBits, true, false, Transparency.TRANSLUCENT, DataBuffer.TYPE_BYTE);
WritableRaster raster = Raster.createBandedRaster(
DataBuffer.TYPE_BYTE, w, h, 4, new Point(0,0));
isPremultiplied = false;
properties = null;
return new BufferedImage(colorModel, raster, isPremultiplied, properties);
}
这会为 Alpha 创建一个单独的栅格(波段),同时也会为每种颜色单独创建一个栅格(波段),因此我最终会得到 4 个波段(4 个栅格),这对于 SWT 图像来说再次无法使用。 是否可以创建具有 2 个波段的带状栅格:一个用于 RGB 或 BRG 颜色,一个仅用于 Alpha?
最佳答案
我不太了解 SWT,但根据我对 API 文档的理解,以下内容应该可行:
诀窍是使用自定义的 DataBuffer 实现,该实现伪装成“带状”缓冲区,但在内部使用交错 RGB 和单独的 alpha 数组的组合进行存储。这与标准 BandedSampleModel
配合得很好。您将失去任何通常应用于使用此模型的 BufferedImage 的特殊(硬件)优化的机会,但这并不重要,因为您无论如何都使用 SWT 进行显示。
我建议您首先创建 SWT 图像,然后将 SWT 图像中的颜色和 alpha 数组“包装”到自定义数据缓冲区中。如果你这样做,Batik应该直接渲染到你的SWT图像,然后你可以扔掉BufferedImage(如果这不切实际,你当然可以反过来做,如下所示)好吧,但您可能需要公开下面的自定义数据缓冲区类的内部数组,以创建 SWT 图像)。
代码(重要部分是 SWTDataBuffer
类和 createImage
方法):
public class SplitDataBufferTest {
/** Custom DataBuffer implementation using separate arrays for RGB and alpha.*/
public static class SWTDataBuffer extends DataBuffer {
private final byte[] rgb; // RGB or BGR interleaved
private final byte[] alpha;
public SWTDataBuffer(byte[] rgb, byte[] alpha) {
super(DataBuffer.TYPE_BYTE, alpha.length, 4); // Masquerade as banded data buffer
if (alpha.length * 3 != rgb.length) {
throw new IllegalArgumentException("Bad RGB/alpha array lengths");
}
this.rgb = rgb;
this.alpha = alpha;
}
@Override
public int getElem(int bank, int i) {
switch (bank) {
case 0:
case 1:
case 2:
return rgb[i * 3 + bank];
case 3:
return alpha[i];
}
throw new IndexOutOfBoundsException(String.format("bank %d >= number of banks, %d", bank, getNumBanks()));
}
@Override
public void setElem(int bank, int i, int val) {
switch (bank) {
case 0:
case 1:
case 2:
rgb[i * 3 + bank] = (byte) val;
return;
case 3:
alpha[i] = (byte) val;
return;
}
throw new IndexOutOfBoundsException(String.format("bank %d >= number of banks, %d", bank, getNumBanks()));
}
}
public static void main(String[] args) {
// These are given from your SWT image
int w = 300;
int h = 200;
byte[] rgb = new byte[w * h * 3];
byte[] alpha = new byte[w * h];
// Create an empty BufferedImage around the SWT image arrays
BufferedImage image = createImage(w, h, rgb, alpha);
// Just to demonstrate that it works
System.out.println("image: " + image);
paintSomething(image);
showIt(image);
}
private static BufferedImage createImage(int w, int h, byte[] rgb, byte[] alpha) {
DataBuffer buffer = new SWTDataBuffer(rgb, alpha);
// SampleModel sampleModel = new BandedSampleModel(DataBuffer.TYPE_BYTE, w, h, 4); // If SWT data is RGB, you can use simpler constructor
SampleModel sampleModel = new BandedSampleModel(DataBuffer.TYPE_BYTE, w, h, w,
new int[] {2, 1, 0, 3}, // Band indices for BGRA
new int[] {0, 0, 0, 0});
WritableRaster raster = Raster.createWritableRaster(sampleModel, buffer, null);
ColorModel colorModel = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_sRGB), true, false, Transparency.TRANSLUCENT, DataBuffer.TYPE_BYTE);
return new BufferedImage(colorModel, raster, colorModel.isAlphaPremultiplied(), null);
}
private static void showIt(final BufferedImage image) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
JFrame frame = new JFrame("Test");
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
JLabel label = new JLabel(new ImageIcon(image));
label.setOpaque(true);
label.setBackground(Color.GRAY);
frame.add(label);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
private static void paintSomething(BufferedImage image) {
int w = image.getWidth();
int h = image.getHeight();
int qw = w / 4;
int qh = h / 4;
Graphics2D g = image.createGraphics();
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g.setColor(Color.ORANGE);
g.fillOval(0, 0, w, h);
g.setColor(Color.RED);
g.fillRect(5, 5, qw, qh);
g.setColor(Color.WHITE);
g.drawString("R", 5, 30);
g.setColor(Color.GREEN);
g.fillRect(5 + 5 + qw, 5, qw, qh);
g.setColor(Color.BLACK);
g.drawString("G", 5 + 5 + qw, 30);
g.setColor(Color.BLUE);
g.fillRect(5 + (5 + qw) * 2, 5, qw, qh);
g.setColor(Color.WHITE);
g.drawString("B", 5 + (5 + qw) * 2, 30);
g.dispose();
}
}
关于java - 如何使用单独的 alpha 栅格创建 BufferedImage,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45351474/