Experimental reading of layers (8 bit channels only ATM).

This commit is contained in:
Harald Kuhr 2009-10-25 17:05:09 +01:00
parent ef7029f306
commit 17e8de8c99

View File

@ -531,6 +531,65 @@ public class PSDImageReader extends ImageReaderBase {
}
}
private void read8bitChannelForLAyer(final int pChannel,
final byte[] pData, final int pBands, final int pBandOffset,
final ColorModel pSourceColorModel,
final byte[] pRow,
final Rectangle pSource, final Rectangle pDest,
final int pXSub, final int pYSub,
final int pWidth, final int pHeight,
final int[] pRowOffsets, final boolean pRLECompressed) throws IOException {
final boolean isCMYK = pSourceColorModel.getColorSpace().getType() == ColorSpace.TYPE_CMYK;
final int colorComponents = pSourceColorModel.getColorSpace().getNumComponents();
for (int y = 0; y < pHeight; y++) {
// TODO: Extra param?
// int length = pRLECompressed ? pRowOffsets[pChannel * pHeight + y] : pWidth;
int length = pRLECompressed ? pRowOffsets[y] : pWidth;
// 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) {
if (pRLECompressed) {
DataInputStream input = PSDUtil.createPackBitsStream(mImageInput, length);
try {
input.readFully(pRow, 0, pWidth);
}
finally {
input.close();
}
}
else {
mImageInput.readFully(pRow, 0, pWidth);
}
// TODO: If banded and not sub sampling/cmyk, we could just copy using System.arraycopy
// TODO: Destination offset...??
// Copy line sub sampled into real data
int offset = (y - pSource.y) / pYSub * pDest.width * pBands + pBandOffset;
for (int x = 0; x < pDest.width; x++) {
byte value = pRow[pSource.x + x * pXSub];
// CMYK values are stored inverted, but alpha is not
if (isCMYK && pChannel < colorComponents) {
value = (byte) (255 - value & 0xff);
}
pData[offset + x * pBands] = value;
}
}
else {
mImageInput.skipBytes(length);
}
if (abortRequested()) {
break;
}
// processImageProgress((pChannel * y * 100) / mHeader.mChannels * mHeader.mHeight);
}
}
private void read1bitChannel(final int pChannel,
final byte[] pData, final int pBands, final int pBandOffset,
final ColorModel pSourceColorModel,
@ -745,15 +804,73 @@ public class PSDImageReader extends ImageReaderBase {
int layers = mImageInput.readShort();
// System.out.println("layers: " + layers);
PSDLayerInfo[] layerInfo = new PSDLayerInfo[Math.abs(layers)];
for (int i = 0; i < layerInfo.length; i++) {
layerInfo[i] = new PSDLayerInfo(mImageInput);
PSDLayerInfo[] layerInfos = new PSDLayerInfo[Math.abs(layers)];
for (int i = 0; i < layerInfos.length; i++) {
layerInfos[i] = new PSDLayerInfo(mImageInput);
// System.out.println("layerInfo[" + i + "]: " + layerInfo[i]);
}
mLayerInfo = Arrays.asList(layerInfo);
mLayerInfo = Arrays.asList(layerInfos);
mImageInput.mark();
ImageTypeSpecifier raw = getRawImageTypeInternal(0);
ImageTypeSpecifier imageType = getImageTypes(0).next();
mImageInput.reset();
for (PSDLayerInfo layerInfo : layerInfos) {
// TODO: Skip if one dimension <= 0
int width = layerInfo.mRight - layerInfo.mLeft;
int height = layerInfo.mBottom - layerInfo.mTop;
// If raw/imageType has no alpha, the layers may still have alpha...
if (layerInfo.mChannelInfo.length > imageType.getNumBands()) {
// But, it could also be just the user mask...
boolean userMask = false;
for (PSDChannelInfo channelInfo : layerInfo.mChannelInfo) {
if (channelInfo.mChannelId == -2) {
userMask = true;
break;
}
}
int newBandNum = layerInfo.mChannelInfo.length - (userMask ? 1 : 0);
// If still more channels, then create new imageType
if (newBandNum > imageType.getNumBands()) {
int[] offs = new int[newBandNum];
for (int i = 0, offsLength = offs.length; i < offsLength; i++) {
offs[i] = offsLength - i;
}
imageType = ImageTypeSpecifier.createInterleaved(imageType.getColorModel().getColorSpace(), offs, imageType.getSampleModel().getDataType(), true, false);
}
}
// Create image
BufferedImage layer = width > 0 && height > 0 ? imageType.createBufferedImage(width, height) : null;
Rectangle source = new Rectangle(width, height);
Rectangle dest = source;
int xsub = 1;
int ysub = 1;
for (PSDChannelInfo channelInfo : layerInfo.mChannelInfo) {
// TODO: If not explicitly needed, skip layers...
if (layer == null || channelInfo.mChannelId == -2) {
// channelId == 2 means "user supplied layer mask", whatever that is...
mImageInput.skipBytes(channelInfo.mLength);
}
else {
int c = channelInfo.mChannelId == -1 ? layerInfo.mChannelInfo.length - 1 : channelInfo.mChannelId;
// System.err.println("channelInfo.mChannelId: " + channelInfo.mChannelId);
// System.err.println("c: " + c);
// System.err.println("layer.getRaster().getNumBands(): " + layer.getRaster().getNumBands());
int[] offsets = null;
for (PSDLayerInfo info : layerInfo) {
for (PSDChannelInfo channelInfo : info.mChannelInfo) {
int compression = mImageInput.readUnsignedShort();
// 0: None, 1: PackBits RLE, 2: Zip, 3: Zip w/prediction
switch (compression) {
@ -762,12 +879,22 @@ public class PSDImageReader extends ImageReaderBase {
break;
case PSD.COMPRESSION_RLE:
// System.out.println("Compression: PackBits RLE");
// NOTE: Offsets will allow us to easily skip rows before AOI
// offsets = new int[mHeader.mChannels * mHeader.mHeight];
offsets = new int[layerInfo.mBottom - layerInfo.mTop];
for (int i = 0; i < offsets.length; i++) {
offsets[i] = mImageInput.readUnsignedShort();
}
break;
case PSD.COMPRESSION_ZIP:
// System.out.println("Compression: ZIP");
// TODO: Skip
// System.out.println("Compression: ZIP");
break;
case PSD.COMPRESSION_ZIP_PREDICTON:
// System.out.println("Compression: ZIP with prediction");
// TODO: Skip
// System.out.println("Compression: ZIP with prediction");
break;
default:
// TODO: Do we care, as we can just skip the data?
@ -779,17 +906,63 @@ public class PSDImageReader extends ImageReaderBase {
}
// TODO: If RLE, the the image data starts with the byte counts
// for all the scan lines in the channel (LayerBottom*LayerTop), with
// for all the scan lines in the channel (LayerBottom-LayerTop), with
// each count stored as a two*byte value.
// if (compression == 1) {
// mImageInput.skipBytes(channelInfo.mLength);
// }
// TODO: Read channel image data (same format as composite image channel data)
mImageInput.skipBytes(channelInfo.mLength - 2);
// if (channelInfo.mLength % 2 != 0) {
// mImageInput.readByte();
// }
// System.out.println("offsets: " + Arrays.toString(offsets));
final WritableRaster raster = layer.getRaster();
// TODO: Conversion if destination cm is not compatible
final ColorModel destCM = layer.getColorModel();
// TODO: This raster is 3-5 times longer than needed, depending on number of channels...
ColorModel sourceCM = raw.getColorModel();
// final WritableRaster rowRaster = sourceCM.createCompatibleWritableRaster(mHeader.mWidth, 1);
final WritableRaster rowRaster = sourceCM.createCompatibleWritableRaster(layer.getWidth(), 1);
// final int channels = rowRaster.getNumBands();
final boolean banded = raster.getDataBuffer().getNumBanks() > 1;
final int interleavedBands = banded ? 1 : raster.getNumBands();
// for (int c = 0; c < channels; c++) {
// for (int c = 0; c < info.mChannelInfo.length; c++) {
int bandOffset = banded ? 0 : interleavedBands - 1 - c;
switch (mHeader.mBits) {
case 1:
byte[] row1 = ((DataBufferByte) rowRaster.getDataBuffer()).getData();
DataBufferByte buffer1 = (DataBufferByte) raster.getDataBuffer();
byte[] data1 = banded ? buffer1.getData(c) : buffer1.getData();
read1bitChannel(c, data1, interleavedBands, bandOffset, sourceCM, row1, source, dest, xsub, ysub, offsets, compression == PSD.COMPRESSION_RLE);
break;
case 8:
byte[] row8 = ((DataBufferByte) rowRaster.getDataBuffer()).getData();
DataBufferByte buffer8 = (DataBufferByte) raster.getDataBuffer();
byte[] data8 = banded ? buffer8.getData(c) : buffer8.getData();
read8bitChannelForLAyer(c, data8, interleavedBands, bandOffset, sourceCM, row8, source, dest, xsub, ysub, layer.getWidth(), layer.getHeight(), offsets, compression == PSD.COMPRESSION_RLE);
break;
case 16:
short[] row16 = ((DataBufferUShort) rowRaster.getDataBuffer()).getData();
DataBufferUShort buffer16 = (DataBufferUShort) raster.getDataBuffer();
short[] data16 = banded ? buffer16.getData(c) : buffer16.getData();
read16bitChannel(c, data16, interleavedBands, bandOffset, sourceCM, row16, source, dest, xsub, ysub, offsets, compression == PSD.COMPRESSION_RLE);
break;
default:
throw new IIOException("Unknown PSD bit depth: " + mHeader.mBits);
}
if (abortRequested()) {
break;
}
}
}
// System.err.println("layer: " + layer);
if (layer != null) {
showIt(layer, layerInfo.mLayerName + " " + layerInfo.mBlendMode.toString());
}
}
@ -798,8 +971,8 @@ public class PSDImageReader extends ImageReaderBase {
long read = mImageInput.getStreamPosition() - pos;
// System.out.println("layerInfoLength: " + layerInfoLength);
// System.out.println("layer info read: " + (read - 4)); // - 4 for the layerInfoLength field itself
long diff = layerInfoLength - (read - 4);
// System.out.println("layer info read: " + (read - 4));
long diff = layerInfoLength - (read - 4); // - 4 for the layerInfoLength field itself
// System.out.println("diff: " + diff);
mImageInput.skipBytes(diff);
@ -939,14 +1112,14 @@ public class PSDImageReader extends ImageReaderBase {
ImageInputStream stream = ImageIO.createImageInputStream(file);
imageReader.setInput(stream);
imageReader.readHeader();
System.out.println("imageReader.mHeader: " + imageReader.mHeader);
// System.out.println("imageReader.mHeader: " + imageReader.mHeader);
imageReader.readImageResources(true);
System.out.println("imageReader.mImageResources: " + imageReader.mImageResources);
// System.out.println("imageReader.mImageResources: " + imageReader.mImageResources);
imageReader.readLayerAndMaskInfo(true);
System.out.println("imageReader.mLayerInfo: " + imageReader.mLayerInfo);
System.out.println("imageReader.mGlobalLayerMask: " + imageReader.mGlobalLayerMask);
// System.out.println("imageReader.mGlobalLayerMask: " + imageReader.mGlobalLayerMask);
if (imageReader.hasThumbnails(0)) {
int thumbnails = imageReader.getNumThumbnails(0);