我正在尝试阅读并显示PNG文件。我对处理8位深度的图像没有问题。我进行如下操作:
BufferedImage result = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
然后我读取每个像素的3 * 8 = 24位,将它们保存在字节data
的数组中,并使用以下命令将它们放入图像中:
for (int y = 0; y < height; y++)
for (int x = 0; x < width; x++)
result.setRGB(x, y, ((data[x * 3 + 0] & 0xff) << 16)
+ ((data[x * 3 + 1] & 0xff) << 8)
+ ((data[x * 3 + 2] & 0xff)));
现在问题出在16位深度图像上。当然,data
现在更大了,对于每个RGB三元组,它包含48位(分为6个字节):来自调试器的data
具有我期望的值。如何设置RGB像素?我是否需要更改BufferedImage声明?也许与:
BufferedImage result = new BufferedImage(width, height, BufferedImage.TYPE_USHORT_565_RGB);
非常感谢!
P.S .:使用PNG标准图像后,图像的颜色类型为2(RGB不带alpha)。
也许我必须使用http://docs.oracle.com/javase/7/docs/api/java/awt/image/ColorModel.html
@@ haraldK指出了正确的方向。我提供了一些工作代码,这些代码来自Java图像库“ icafe”的“ PNGReader”。
if(bitsPerPixel == 16) {
if(interlace_method==NON_INTERLACED)
spixels = generate16BitRGBPixels(compr_data, false);
else {
spixels = generate16BitRGBInterlacedPixels(compr_data, false);
}
int[] off = {0, 1, 2}; //band offset, we have 3 bands
int numOfBands = 3;
boolean hasAlpha = false;
int trans = Transparency.OPAQUE;
int[] nBits = {16, 16, 16};
if(alpha != null) { // Deal with single color transparency
off = new int[] {0, 1, 2, 3}; //band offset, we have 4 bands
numOfBands = 4;
hasAlpha = true;
trans = Transparency.TRANSLUCENT;
nBits = new int[] {16, 16, 16, 16};
}
db = new DataBufferUShort(spixels, spixels.length);
raster = Raster.createInterleavedRaster(db, width, height, width*numOfBands, numOfBands, off, null);
cm = new ComponentColorModel(colorSpace, nBits, hasAlpha, false, trans, DataBuffer.TYPE_USHORT);
}
return new BufferedImage(cm, raster, false, null);
这里是generate16BitRGBPixels()方法:
private short[] generate16BitRGBPixels(byte[] compr_data, boolean fullAlpha) throws Exception {
//
int bytesPerPixel = 0;
byte[] pixBytes;
if (fullAlpha)
bytesPerPixel = 8;
else
bytesPerPixel = 6;
bytesPerScanLine = width*bytesPerPixel;
// Now inflate the data.
pixBytes = new byte[height * bytesPerScanLine];
// Wrap an InflaterInputStream with a bufferedInputStream to speed up reading
BufferedInputStream bis = new BufferedInputStream(new InflaterInputStream(new ByteArrayInputStream(compr_data)));
apply_defilter(bis, pixBytes, height, bytesPerPixel, bytesPerScanLine);
short[] spixels = null;
if(alpha != null) { // Deal with single color transparency
spixels = new short[width*height*4];
short redMask = (short)((alpha[1]&0xff)|(alpha[0]&0xff)<<8);
short greenMask = (short)((alpha[3]&0xff)|(alpha[2]&0xff)<<8);;
short blueMask = (short)((alpha[5]&0xff)|(alpha[4]&0xff)<<8);
for(int i = 0, index = 0; i < pixBytes.length; index += 4) {
short red = (short)((pixBytes[i++]&0xff)<<8|(pixBytes[i++]&0xff));
short green = (short)((pixBytes[i++]&0xff)<<8|(pixBytes[i++]&0xff));
short blue = (short)((pixBytes[i++]&0xff)<<8|(pixBytes[i++]&0xff));
spixels[index] = red;
spixels[index + 1] = green;
spixels[index + 2] = blue;
if(spixels[index] == redMask && spixels[index + 1] == greenMask && spixels[index + 2] == blueMask) {
spixels[index + 3] = (short)0x0000;
} else {
spixels[index + 3] = (short)0xffff;
}
}
} else
spixels = ArrayUtils.toShortArray(pixBytes, true);
return spixels;
}
和ArrayUtils.toShortArray()方法:
public static short[] toShortArray(byte[] data, int offset, int len, boolean bigEndian) {
ByteBuffer byteBuffer = ByteBuffer.wrap(data, offset, len);
if (bigEndian) {
byteBuffer.order(ByteOrder.BIG_ENDIAN);
} else {
byteBuffer.order(ByteOrder.LITTLE_ENDIAN);
}
ShortBuffer shortBuf = byteBuffer.asShortBuffer();
short[] array = new short[shortBuf.remaining()];
shortBuf.get(array);
return array;
}
无法使用BufferedImage.TYPE_...
来创建每个样本16位(或每个像素48位)的图像(没有这样的常数)。 TYPE_USHORT_565_RGB
创建一个每像素16位的图像,分别具有5位(红色),6位(绿色)和5位(蓝色)的样本。我认为这些USHORT RGB值是某些计算实际上具有16位显示(也称为“数千种颜色”)选项时的剩余值。
要实际创建每个样本具有16位的图像,您需要做的是:
ColorModel cm;
WritableRaster raster;
BufferedImage result = new BufferedImage(cm, raster, cm.isAlphaPremultiplied(), null);
[栅格是从类型为DataBufferUShort
的数据缓冲区创建的,该数据缓冲区具有3个存储区和具有3个波段的BandedSampleModel
,或者使用单个存储区和PixelInterleavedSampleModel
的pixelStride
为3,scanLineStride
为3 * width
和bandOffsets
{0, 1, 2}
。
这里是完整的样本,使用交错样本模型:
ColorSpace sRGB = ColorSpace.getInstance(ColorSpace.CS_sRGB)
ColorModel cm = new ComponentColorModel(sRGB, false, false, Transparency.OPAQUE, DataBuffer.TYPE_USHORT);
WritableRaster raster = Raster.createInterleavedRaster(DataBuffer.TYPE_USHORT, w, h, 3, null);
BufferedImage rgb = new BufferedImage(cm, raster, cm.isAlphaPremultiplied(), null);
PS:露出数据缓冲区后,您可以直接访问short
样本,以操纵像素。这比使用BufferedImage.getRGB(...)/setRGB(...)
快得多,并且每个样本的精度将保持原始的16位。 BufferedImage.getRGB(...)
将把每个样本的像素值转换为32位像素/ 8位,从而失去额外的精度。