如何使用非 ARGB 颜色空间设置 Java BufferedImages 中的像素?

How can I set pixels in Java BufferedImages using non-ARGB color spaces?

我正在编写一个需要使用 16 位“5-5-5”RGB 颜色(即,每种颜色 5 位和一位填充)的应用程序。为了处理这些图像,我使用了 AWT 提供的 BufferedImage class。 BufferedImage class 通过采用 ColorModel 对象或预定义图像类型常量特别允许使用非 RGB 颜色 space - 其中之一是我使用的 5-5-5 像素格式需要。

我的问题是:BufferedImage“setRGB()”方法在其描述中指出所提供的颜色值“假定为默认 RGB 颜色模型 TYPE_INT_ARGB 和默认 sRGB 颜色 space"(根据 BufferedImage 文档页面)。似乎也没有其他方法接受为不同颜色 space 设计的值。

有没有办法直接用 BufferedImage 使用我的非标准颜色 space,或者我是否必须依赖 class 的内部颜色转换机制来处理我所有的颜色?(或者我只是 misreading/misunderstanding 了解 class 的工作原理?)

BufferedImage.TYPE_USHORT_555_RGB 仍然使用完全标准的 RGB 颜色 space(实际上,它使用 sRGB),所以我认为不会有不同的 颜色 space就是你要找的。

如果要在Java中进行绘画或其他操作,只需使用setRGB/getRGB()createGraphics()/Grapics2D等常规方法即可。所有内容都将为您正确地转换为打包的 USHORT_555_RGB 格式。

例如:

BufferedImage image = new BufferedImage(w, h, BufferedImage.TYPE_USHORT_555_RGB);

// Do some custom painting
Graphics2D g = image.createGraphics();
g.drawImage(otherImage, 0, 0, null); // image type here does not matter
g.setColor(Color.ORANGE);            // Color in sRGB, but does not matter
g.fillOval(0, 0, w, h);
g.dispose();

image.setRGB(0, h/2, w, 1, new int[w]); // Silly way to create a horizontal black line at the center of the image... Don't do this, use fillRect(0, h/2, 1, w)! ;-)  

// image will still be USHORT_555_RGB *internally*

但是,如果您有 USHORT_555_RGB 格式的像素数据(即来自外部 library/api/service),将这些值直接设置为 raster/databuffer.或者,如果您需要将像素值传回相同的 library/api/service.

例如,使用 Raster:

BufferedImage image = new BufferedImage(w, h, BufferedImage.TYPE_USHORT_555_RGB);

// Some fictional API. It's assumed that data.length == w * h
short[] apiPixels = api.getPixelsUSHORT_555_RGB(w, h);

WritableRaster raster = image.getRaster();

// Set short values to image
raster.setDataElements(0, 0, w, h, apiPixels);
        
// Get short values from image
short[] pixels = (short[]) raster.getDataElements(0, 0, w, h, null); // TYPE_USHORT_555_RGB -> always short[]
api.setPixels(pixels, w, h); // Another fictional API

或者,也可以使用 DataBuffer:

BufferedImage image = new BufferedImage(w, h, BufferedImage.TYPE_USHORT_555_RGB);

// Some fictional API. It's assumed that data.length == w * h
short[] apiPixels = api.getPixelsUSHORT_555_RGB(w, h);

DataBufferUShort buffer = (DataBufferUShort) image.getRaster().getDataBuffer(); // TYPE_USHORT_555_RGB -> always DataBufferUShort

// Set short values to image
System.arraycopy(apiPixels, 0, buffer.getData(), 0, apiPixels.length);

// Get short values from image
api.setPixels(buffer.getData(), w, h);

在大多数情况下,使用哪种方法并不重要,但第一种方法(仅使用 Raster)可能会保持对图像的管理,这将使图像 显示 在 Java 过程中在屏幕上更快。


PS:如果不同的颜色 space 确实是您所需要的(即来自外部 library/api/service 的像素阵列使用不同的颜色 space,并且您需要查看此颜色 space 中的像素),您可以使用自定义颜色 space 在 USHORT_555_RGB 样式中创建 BufferedImage ] 像这样:

// Either use one of the built-in color spaces, or load one from disk
ColorSpace colorSpace = ColorSpace.getInstance(ColorSpace.CS_LINEAR_RGB);
ColorSpace colorSpaceToo = new ICC_ColorSpace(ICC_Profile.getInstance(Files.newInputStream(new File("/path/to/custom_rgb_profile.icc").toPath())));

// Create a color model using your color space, TYPE_USHORT and 5/5/5 mask, no transparency
ColorModel colorModel = new DirectColorModel(colorSpace, 15, 0x7C00, 0x03E0, 0x001F, 0, false, DataBuffer.TYPE_USHORT);

// And finally, create an image from the color model and a compatible raster
BufferedImage imageToo = new BufferedImage(colorModel, colorModel.createCompatibleWritableRaster(w, h), colorModel.isAlphaPremultiplied(), null);

请记住,由于 Java2D 图形操作和 setRGB/getRGB 仍在使用 sRGB,现在对图像的所有操作都将在颜色 space 和颜色之间来回转换sRGB。性能不会那么好。