Implemented reading of 16 bit channels.

This commit is contained in:
Harald Kuhr 2009-09-29 18:29:50 +02:00
parent eea6fd38fc
commit 3e972495db

View File

@ -110,6 +110,11 @@ public class PSDImageReader extends ImageReaderBase {
List<ImageTypeSpecifier> types = new ArrayList<ImageTypeSpecifier>(); List<ImageTypeSpecifier> types = new ArrayList<ImageTypeSpecifier>();
switch (mHeader.mMode) { switch (mHeader.mMode) {
// TODO: Fix reading of 1 and 16 bit...
case PSD.COLOR_MODE_MONOCHROME:
types.add(ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_BYTE_BINARY));
break;
case PSD.COLOR_MODE_INDEXED: case PSD.COLOR_MODE_INDEXED:
if (mHeader.mChannels == 1 && mHeader.mBits == 8) { if (mHeader.mChannels == 1 && mHeader.mBits == 8) {
types.add(IndexedImageTypeSpecifier.createFromIndexColorModel(mColorData.getIndexColorModel())); types.add(IndexedImageTypeSpecifier.createFromIndexColorModel(mColorData.getIndexColorModel()));
@ -145,6 +150,14 @@ public class PSDImageReader extends ImageReaderBase {
// types.add(ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_4BYTE_ABGR)); // types.add(ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_4BYTE_ABGR));
types.add(ImageTypeSpecifier.createInterleaved(cs, new int[] {3, 2, 1, 0}, DataBuffer.TYPE_BYTE, true, false)); types.add(ImageTypeSpecifier.createInterleaved(cs, new int[] {3, 2, 1, 0}, DataBuffer.TYPE_BYTE, true, false));
} }
else if (mHeader.mChannels == 3 && mHeader.mBits == 16) {
// types.add(ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_3BYTE_BGR));
types.add(ImageTypeSpecifier.createInterleaved(cs, new int[] {2, 1, 0}, DataBuffer.TYPE_USHORT, false, false));
}
else if (mHeader.mChannels >= 4 && mHeader.mBits == 16) {
// types.add(ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_4BYTE_ABGR));
types.add(ImageTypeSpecifier.createInterleaved(cs, new int[] {3, 2, 1, 0}, DataBuffer.TYPE_USHORT, true, false));
}
else { else {
throw new IIOException("Unsupported channel count/bit depth for RGB PSD: " + mHeader.mChannels + " channels/" + mHeader.mBits + " bits"); throw new IIOException("Unsupported channel count/bit depth for RGB PSD: " + mHeader.mChannels + " channels/" + mHeader.mBits + " bits");
} }
@ -167,6 +180,14 @@ public class PSDImageReader extends ImageReaderBase {
// types.add(ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_4BYTE_ABGR)); // types.add(ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_4BYTE_ABGR));
types.add(ImageTypeSpecifier.createInterleaved(cs, new int[]{4, 3, 2, 1, 0}, DataBuffer.TYPE_BYTE, true, false)); types.add(ImageTypeSpecifier.createInterleaved(cs, new int[]{4, 3, 2, 1, 0}, DataBuffer.TYPE_BYTE, true, false));
} }
else if (mHeader.mChannels == 4 && mHeader.mBits == 16) {
// types.add(ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_3BYTE_BGR));
types.add(ImageTypeSpecifier.createInterleaved(cs, new int[]{3, 2, 1, 0}, DataBuffer.TYPE_USHORT, false, false));
}
else if (mHeader.mChannels == 5 && mHeader.mBits == 16) {
// types.add(ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_4BYTE_ABGR));
types.add(ImageTypeSpecifier.createInterleaved(cs, new int[]{4, 3, 2, 1, 0}, DataBuffer.TYPE_USHORT, true, false));
}
else { else {
throw new IIOException("Unsupported channel count/bit depth for CMYK PSD: " + mHeader.mChannels + " channels/" + mHeader.mBits + " bits"); throw new IIOException("Unsupported channel count/bit depth for CMYK PSD: " + mHeader.mChannels + " channels/" + mHeader.mBits + " bits");
} }
@ -238,9 +259,6 @@ public class PSDImageReader extends ImageReaderBase {
// TODO: Maybe a banded raster would be easier than interleaved? We could still convert to interleaved in CCOp // TODO: Maybe a banded raster would be easier than interleaved? We could still convert to interleaved in CCOp
WritableRaster raster = image.getRaster(); WritableRaster raster = image.getRaster();
if (!(raster.getDataBuffer() instanceof DataBufferByte)) {
throw new IIOException("Unsupported raster type: " + raster);
}
final int xSub; final int xSub;
final int ySub; final int ySub;
@ -260,7 +278,6 @@ public class PSDImageReader extends ImageReaderBase {
// This code works fine for images with channel depth = 8 // This code works fine for images with channel depth = 8
switch (compression) { switch (compression) {
case PSD.COMPRESSION_NONE: case PSD.COMPRESSION_NONE:
read8bitData(raster, image.getColorModel(), source, dest, xSub, ySub, offsets, false);
break; break;
case PSD.COMPRESSION_RLE: case PSD.COMPRESSION_RLE:
// NOTE: Offsets will allow us to easily skip rows before AOI // NOTE: Offsets will allow us to easily skip rows before AOI
@ -268,7 +285,6 @@ public class PSDImageReader extends ImageReaderBase {
for (int i = 0; i < offsets.length; i++) { for (int i = 0; i < offsets.length; i++) {
offsets[i] = mImageInput.readUnsignedShort(); offsets[i] = mImageInput.readUnsignedShort();
} }
read8bitData(raster, image.getColorModel(), source, dest, xSub, ySub, offsets, true);
break; break;
case PSD.COMPRESSION_ZIP: case PSD.COMPRESSION_ZIP:
// TODO: Could probably use the ZIPDecoder (DeflateDecoder) here.. // TODO: Could probably use the ZIPDecoder (DeflateDecoder) here..
@ -280,6 +296,21 @@ public class PSDImageReader extends ImageReaderBase {
throw new IIOException("Unknown compression type: " + compression); throw new IIOException("Unknown compression type: " + compression);
} }
switch (mHeader.mBits) {
case 1:
read1bitData(raster, image.getColorModel(), source, dest, xSub, ySub, offsets, compression == PSD.COMPRESSION_RLE);
break;
case 8:
read8bitData(raster, image.getColorModel(), source, dest, xSub, ySub, offsets, compression == PSD.COMPRESSION_RLE);
break;
case 16:
read16bitData(raster, image.getColorModel(), source, dest, xSub, ySub, offsets, compression == PSD.COMPRESSION_RLE);
break;
default:
throw new IIOException("Unknown bit depth: " + mHeader.mBits);
}
if (abortRequested()) { if (abortRequested()) {
processReadAborted(); processReadAborted();
} }
@ -290,6 +321,91 @@ public class PSDImageReader extends ImageReaderBase {
return image; return image;
} }
private void read16bitData(final WritableRaster pRaster, final ColorModel pDestinationColorModel,
final Rectangle pSource, final Rectangle pDest,
final int pXSub, final int pYSub,
final int[] pRowOffsets, final boolean pRLECompressed) throws IOException
{
final int channels = pRaster.getNumBands();
final short[] data = ((DataBufferUShort) pRaster.getDataBuffer()).getData();
// TODO: FixMe: Use real source color model from native (raw) image type, and convert if needed
ColorModel sourceColorModel = pDestinationColorModel;
WritableRaster rowRaster = sourceColorModel.createCompatibleWritableRaster(mHeader.mWidth, 1);
final short[] row = ((DataBufferUShort) rowRaster.getDataBuffer()).getData();
final boolean isCMYK = sourceColorModel.getColorSpace().getType() == ColorSpace.TYPE_CMYK;
final int colorComponents = sourceColorModel.getColorSpace().getNumComponents();
int x = 0, y = 0, c = 0;
try {
for (c = 0; c < channels; c++) {
for (y = 0; y < mHeader.mHeight; y++) {
// Length is in shorts!?
int length = 2 * (pRLECompressed ? pRowOffsets[c * mHeader.mHeight + y] : mHeader.mWidth);
// TODO: Sometimes need to read the line y == source.y + source.height...
// Read entire line, if within source region and sampling
if (y >= pSource.y && y < pSource.y + pSource.height && y % pYSub == 0) {
DataInput input = pRLECompressed ? PSDUtil.createPackBitsStream(mImageInput, length) : mImageInput;
for (x = 0; x < mHeader.mWidth; x++) {
short value = input.readShort();
// CMYK values are stored inverted, but alpha is not
if (isCMYK && c < colorComponents) {
value = (short) (65535 - value & 0xffff);
}
row[x] = value;
}
// TODO: Destination offset...??
// Copy line sub sampled into real data
int offset = (y - pSource.y) / pYSub * pDest.width * channels + (channels - 1 - c);
for (int i = 0; i < pDest.width; i++) {
data[offset + i * channels] = row[pSource.x + i * pXSub];
}
if (pRLECompressed) {
((DataInputStream) input).close();
}
}
else {
mImageInput.skipBytes(length);
}
if (abortRequested()) {
break;
}
processImageProgress((c * y * 100) / mHeader.mChannels * mHeader.mHeight);
}
if (abortRequested()) {
break;
}
}
}
catch (IOException e) {
System.err.println("c: " + c);
System.err.println("y: " + y);
System.err.println("x: " + x);
throw e;
}
catch (IndexOutOfBoundsException e) {
e.printStackTrace();
System.out.println("data.length: " + data.length);
System.err.println("c: " + c);
System.err.println("y: " + y);
System.err.println("x: " + x);
throw e;
}
// TODO: Alpha in 16 bits samples!?
// Compose out the background of the semi-transparent pixels, as PS somehow has the background composed in
// decomposeAlpha(sourceColorModel, data, pDest.width, pDest.height, channels);
}
private void read8bitData(final WritableRaster pRaster, final ColorModel pDestinationColorModel, private void read8bitData(final WritableRaster pRaster, final ColorModel pDestinationColorModel,
final Rectangle pSource, final Rectangle pDest, final Rectangle pSource, final Rectangle pDest,
final int pXSub, final int pYSub, final int pXSub, final int pYSub,
@ -373,6 +489,95 @@ public class PSDImageReader extends ImageReaderBase {
decomposeAlpha(sourceColorModel, data, pDest.width, pDest.height, channels); decomposeAlpha(sourceColorModel, data, pDest.width, pDest.height, channels);
} }
private void read1bitData(final WritableRaster pRaster, final ColorModel pDestinationColorModel,
final Rectangle pSource, final Rectangle pDest,
final int pXSub, final int pYSub,
final int[] pRowOffsets, final boolean pRLECompressed) throws IOException {
final byte[] data = ((DataBufferByte) pRaster.getDataBuffer()).getData();
// TODO: FixMe: Use real source color model from native (raw) image type, and convert if needed
ColorModel sourceColorModel = pDestinationColorModel;
WritableRaster rowRaster = sourceColorModel.createCompatibleWritableRaster(mHeader.mWidth, 1);
final byte[] row = ((DataBufferByte) rowRaster.getDataBuffer()).getData();
final int destWidth = (pDest.width + 7) / 8;
int x = 0, y = 0;
try {
for (y = 0; y < mHeader.mHeight; y++) {
int length = pRLECompressed ? pRowOffsets[y] : mHeader.mWidth;
// TODO: Sometimes need to read the line y == source.y + source.height...
// Read entire line, if within source region and sampling
if (y >= pSource.y && y < pSource.y + pSource.height && y % pYSub == 0) {
DataInput input = pRLECompressed ? PSDUtil.createPackBitsStream(mImageInput, length) : mImageInput;
for (x = 0; x < row.length; x++) {
byte value = input.readByte();
row[x] = (byte) (~value & 0xff); // Invert bits to match Java default monochrome
}
// TODO: Destination offset...??
int offset = (y - pSource.y) / pYSub * destWidth;
if (pXSub == 1) {
// Fast normal case, no sub sampling
for (int i = 0; i < destWidth; i++) {
data[offset + i] = row[pSource.x + i * pXSub];
}
}
else {
// Copy line sub sampled into real data
int bitPos = pSource.x;
for (int i = 0; i < destWidth; i++) {
byte result = 0;
for (int j = 0; j < 8; j++) {
int mask = 0x80 >> bitPos % 8;
System.out.println("result: " + Integer.toBinaryString(result & 0xff));
System.out.println("mask: " + Integer.toBinaryString(mask));
System.out.println("bitPos: " + bitPos);
int pos = pSource.x / 8 + i + bitPos / 8;
System.out.println("pos: " + pos);
result |= (row[pos] & mask) >> bitPos / 8;
bitPos += pXSub;
}
System.out.println("--> result: " + Integer.toBinaryString(result & 0xff));
System.out.println();
data[offset + i] = result;
}
}
if (pRLECompressed) {
((DataInputStream) input).close();
}
}
else {
mImageInput.skipBytes(length);
}
if (abortRequested()) {
break;
}
processImageProgress((y * 100) / mHeader.mHeight);
}
}
catch (IOException e) {
System.err.println("y: " + y);
System.err.println("x: " + x);
throw e;
}
catch (IndexOutOfBoundsException e) {
e.printStackTrace();
System.out.println("data.length: " + data.length);
System.err.println("y: " + y);
System.err.println("x: " + x);
throw e;
}
}
private void decomposeAlpha(final ColorModel pModel, final byte[] pData, private void decomposeAlpha(final ColorModel pModel, final byte[] pData,
final int pWidth, final int pHeight, final int pChannels) throws IOException final int pWidth, final int pHeight, final int pChannels) throws IOException
{ {
@ -577,10 +782,12 @@ public class PSDImageReader extends ImageReaderBase {
long start = System.currentTimeMillis(); long start = System.currentTimeMillis();
ImageReadParam param = new ImageReadParam(); ImageReadParam param = new ImageReadParam();
if (pArgs.length > 1) {
// param.setSourceRegion(new Rectangle(200, 200, 400, 400)); // param.setSourceRegion(new Rectangle(200, 200, 400, 400));
// param.setSourceRegion(new Rectangle(300, 200)); // param.setSourceRegion(new Rectangle(300, 200));
param.setSourceSubsampling(3, 3, 0, 0); param.setSourceSubsampling(3, 3, 0, 0);
// param.setSourceSubsampling(2, 2, 0, 0); // param.setSourceSubsampling(2, 2, 0, 0);
}
BufferedImage image = imageReader.read(0, param); BufferedImage image = imageReader.read(0, param);
System.out.println("time: " + (System.currentTimeMillis() - start)); System.out.println("time: " + (System.currentTimeMillis() - start));
System.out.println("image: " + image); System.out.println("image: " + image);