mirror of
https://github.com/haraldk/TwelveMonkeys.git
synced 2026-04-30 00:00:01 -04:00
Mainly new code standard.
A few changes that should have been committed earlier.. :-/
This commit is contained in:
+109
-91
@@ -30,6 +30,7 @@ package com.twelvemonkeys.imageio.plugins.psd;
|
||||
|
||||
import com.twelvemonkeys.image.ImageUtil;
|
||||
import com.twelvemonkeys.imageio.ImageReaderBase;
|
||||
import com.twelvemonkeys.imageio.color.ColorSpaces;
|
||||
import com.twelvemonkeys.imageio.util.IndexedImageTypeSpecifier;
|
||||
import com.twelvemonkeys.xml.XMLSerializer;
|
||||
import org.w3c.dom.Node;
|
||||
@@ -63,6 +64,7 @@ import java.util.List;
|
||||
*/
|
||||
// TODO: Implement ImageIO meta data interface
|
||||
// TODO: Allow reading the extra alpha channels (index after composite data)
|
||||
// TODO: Figure out of we should assume Adobe RGB (1998) color model, if no embedded profile?
|
||||
// TODO: Support for PSDVersionInfo hasRealMergedData=false (no real composite data, layers will be in index 0)
|
||||
// TODO: Support for API for reading separate layers (index after composite data, and optional alpha channels)
|
||||
// TODO: Consider Romain Guy's Java 2D implementation of PS filters for the blending modes in layers
|
||||
@@ -151,6 +153,7 @@ public class PSDImageReader extends ImageReaderBase {
|
||||
case PSD.COLOR_MODE_RGB:
|
||||
cs = getEmbeddedColorSpace();
|
||||
if (cs == null) {
|
||||
// TODO: Should probably be Adobe RGB (1998), not sRGB. Or..?
|
||||
cs = ColorSpace.getInstance(ColorSpace.CS_sRGB);
|
||||
}
|
||||
|
||||
@@ -174,7 +177,7 @@ public class PSDImageReader extends ImageReaderBase {
|
||||
case PSD.COLOR_MODE_CMYK:
|
||||
cs = getEmbeddedColorSpace();
|
||||
if (cs == null) {
|
||||
cs = CMYKColorSpace.getInstance();
|
||||
cs = ColorSpaces.getColorSpace(ColorSpaces.CS_GENERIC_CMYK);
|
||||
}
|
||||
|
||||
if (mHeader.mChannels == 4 && mHeader.mBits == 8) {
|
||||
@@ -198,6 +201,7 @@ public class PSDImageReader extends ImageReaderBase {
|
||||
// TODO: Implement
|
||||
case PSD.COLOR_MODE_LAB:
|
||||
// TODO: Implement
|
||||
// TODO: If there's a color profile embedded, it should be easy, otherwise we're out of luck...
|
||||
default:
|
||||
throw new IIOException(
|
||||
String.format("Unsupported PSD MODE: %s (%d channels/%d bits)", mHeader.mMode, mHeader.mChannels, mHeader.mBits)
|
||||
@@ -219,12 +223,26 @@ public class PSDImageReader extends ImageReaderBase {
|
||||
case PSD.COLOR_MODE_RGB:
|
||||
// Prefer interleaved versions as they are much faster to display
|
||||
if (mHeader.mChannels == 3 && mHeader.mBits == 8) {
|
||||
// Basically same as BufferedImage.TYPE_3BYTE_BGR
|
||||
types.add(ImageTypeSpecifier.createInterleaved(cs, new int[] {2, 1, 0}, DataBuffer.TYPE_BYTE, false, false));
|
||||
// TODO: ColorConvertOp to CS_sRGB
|
||||
// TODO: Integer raster
|
||||
// types.add(ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.INT_RGB));
|
||||
types.add(ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_3BYTE_BGR));
|
||||
|
||||
if (!cs.isCS_sRGB()) {
|
||||
// Basically BufferedImage.TYPE_3BYTE_BGR, with corrected ColorSpace. Possibly slow.
|
||||
types.add(ImageTypeSpecifier.createInterleaved(cs, new int[] {2, 1, 0}, DataBuffer.TYPE_BYTE, false, false));
|
||||
}
|
||||
}
|
||||
else if (mHeader.mChannels >= 4 && mHeader.mBits == 8) {
|
||||
// Basically same as BufferedImage.TYPE_4BYTE_ABGR
|
||||
types.add(ImageTypeSpecifier.createInterleaved(cs, new int[] {3, 2, 1, 0}, DataBuffer.TYPE_BYTE, true, false));
|
||||
// TODO: ColorConvertOp to CS_sRGB
|
||||
// TODO: Integer raster
|
||||
// types.add(ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.INT_ARGB));
|
||||
types.add(ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_4BYTE_ABGR));
|
||||
//
|
||||
if (!cs.isCS_sRGB()) {
|
||||
// Basically BufferedImage.TYPE_4BYTE_ABGR, with corrected ColorSpace. Possibly slow.
|
||||
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.createInterleaved(cs, new int[] {2, 1, 0}, DataBuffer.TYPE_USHORT, false, false));
|
||||
@@ -235,9 +253,11 @@ public class PSDImageReader extends ImageReaderBase {
|
||||
break;
|
||||
case PSD.COLOR_MODE_CMYK:
|
||||
// Prefer interleaved versions as they are much faster to display
|
||||
// TODO: ColorConvertOp to CS_sRGB
|
||||
// TODO: We should convert these to their RGB equivalents while reading for the common-case,
|
||||
// as Java2D is extremely slow displaying custom images.
|
||||
// Converting to RGB is also correct behaviour, according to the docs.
|
||||
// Doing this, will require rewriting the image reading, as the raw image data is channelled, not interleaved :-/
|
||||
if (mHeader.mChannels == 4 && mHeader.mBits == 8) {
|
||||
types.add(ImageTypeSpecifier.createInterleaved(cs, new int[] {3, 2, 1, 0}, DataBuffer.TYPE_BYTE, false, false));
|
||||
}
|
||||
@@ -255,7 +275,7 @@ public class PSDImageReader extends ImageReaderBase {
|
||||
// Just stick to the raw type
|
||||
}
|
||||
|
||||
// Finally add the
|
||||
// Finally add the raw type
|
||||
types.add(rawType);
|
||||
|
||||
return types.iterator();
|
||||
@@ -275,7 +295,7 @@ public class PSDImageReader extends ImageReaderBase {
|
||||
}
|
||||
}
|
||||
|
||||
mColorSpace = profile == null ? null : new ICC_ColorSpace(profile);
|
||||
mColorSpace = profile == null ? null : ColorSpaces.createColorSpace(profile);
|
||||
}
|
||||
|
||||
return mColorSpace;
|
||||
@@ -307,6 +327,8 @@ public class PSDImageReader extends ImageReaderBase {
|
||||
// Otherwise, copy "through" ColorModel?
|
||||
// Copy pixels from temp raster
|
||||
// If possible, leave the destination image "untouched" (accelerated)
|
||||
// See Jim Grahams comments:
|
||||
// http://forums.java.net/jive/message.jspa?messageID=295758#295758
|
||||
|
||||
// TODO: Doing a per line color convert will be expensive, as data is channelled...
|
||||
// Will need to either convert entire image, or skip back/forth between channels...
|
||||
@@ -336,7 +358,7 @@ public class PSDImageReader extends ImageReaderBase {
|
||||
processImageStarted(pIndex);
|
||||
|
||||
int[] byteCounts = null;
|
||||
int compression = mImageInput.readShort();
|
||||
int compression = imageInput.readShort();
|
||||
// TODO: Need to make sure compression is set in metadata, even without reading the image data!
|
||||
mMetadata.mCompression = compression;
|
||||
|
||||
@@ -347,7 +369,7 @@ public class PSDImageReader extends ImageReaderBase {
|
||||
// NOTE: Byte counts will allow us to easily skip rows before AOI
|
||||
byteCounts = new int[mHeader.mChannels * mHeader.mHeight];
|
||||
for (int i = 0; i < byteCounts.length; i++) {
|
||||
byteCounts[i] = mImageInput.readUnsignedShort();
|
||||
byteCounts[i] = imageInput.readUnsignedShort();
|
||||
}
|
||||
break;
|
||||
case PSD.COMPRESSION_ZIP:
|
||||
@@ -400,24 +422,15 @@ public class PSDImageReader extends ImageReaderBase {
|
||||
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, mHeader.mChannels, data1, interleavedBands, bandOffset, pSourceCM, row1, pSource, pDest, pXSub, pYSub, mHeader.mWidth, mHeader.mHeight, pByteCounts, pCompression == PSD.COMPRESSION_RLE);
|
||||
read1bitChannel(c, mHeader.mChannels, raster.getDataBuffer(), interleavedBands, bandOffset, pSourceCM, row1, pSource, pDest, pXSub, pYSub, mHeader.mWidth, mHeader.mHeight, pByteCounts, pCompression == 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();
|
||||
|
||||
read8bitChannel(c, mHeader.mChannels, data8, interleavedBands, bandOffset, pSourceCM, row8, pSource, pDest, pXSub, pYSub, mHeader.mWidth, mHeader.mHeight, pByteCounts, c * mHeader.mHeight, pCompression == PSD.COMPRESSION_RLE);
|
||||
read8bitChannel(c, mHeader.mChannels, raster.getDataBuffer(), interleavedBands, bandOffset, pSourceCM, row8, pSource, pDest, pXSub, pYSub, mHeader.mWidth, mHeader.mHeight, pByteCounts, c * mHeader.mHeight, pCompression == 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, mHeader.mChannels, data16, interleavedBands, bandOffset, pSourceCM, row16, pSource, pDest, pXSub, pYSub, mHeader.mWidth, mHeader.mHeight, pByteCounts, c * mHeader.mHeight, pCompression == PSD.COMPRESSION_RLE);
|
||||
read16bitChannel(c, mHeader.mChannels, raster.getDataBuffer(), interleavedBands, bandOffset, pSourceCM, row16, pSource, pDest, pXSub, pYSub, mHeader.mWidth, mHeader.mHeight, pByteCounts, c * mHeader.mHeight, pCompression == PSD.COMPRESSION_RLE);
|
||||
break;
|
||||
default:
|
||||
throw new IIOException(String.format("Unknown PSD bit depth: %s", mHeader.mBits));
|
||||
@@ -430,12 +443,16 @@ public class PSDImageReader extends ImageReaderBase {
|
||||
|
||||
if (mHeader.mBits == 8) {
|
||||
// Compose out the background of the semi-transparent pixels, as PS somehow has the background composed in
|
||||
decomposeAlpha(destCM, (DataBufferByte) raster.getDataBuffer(), pDest.width, pDest.height, raster.getNumBands());
|
||||
decomposeAlpha(destCM, raster.getDataBuffer(), pDest.width, pDest.height, raster.getNumBands());
|
||||
}
|
||||
}
|
||||
|
||||
private void processImageProgressForChannel(int channel, int channelCount, int y, int height) {
|
||||
processImageProgress(100f * channel / channelCount + 100f * y / (height * channelCount));
|
||||
}
|
||||
|
||||
private void read16bitChannel(final int pChannel, final int pChannelCount,
|
||||
final short[] pData, final int pBands, final int pBandOffset,
|
||||
final DataBuffer pData, final int pBands, final int pBandOffset,
|
||||
final ColorModel pSourceColorModel,
|
||||
final short[] pRow,
|
||||
final Rectangle pSource, final Rectangle pDest,
|
||||
@@ -446,6 +463,7 @@ public class PSDImageReader extends ImageReaderBase {
|
||||
|
||||
final boolean isCMYK = pSourceColorModel.getColorSpace().getType() == ColorSpace.TYPE_CMYK;
|
||||
final int colorComponents = pSourceColorModel.getColorSpace().getNumComponents();
|
||||
final boolean banded = pData.getNumBanks() > 1;
|
||||
|
||||
for (int y = 0; y < pChannelHeight; y++) {
|
||||
// NOTE: Length is in *16 bit values* (shorts)
|
||||
@@ -455,7 +473,7 @@ public class PSDImageReader extends ImageReaderBase {
|
||||
// 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);
|
||||
DataInputStream input = PSDUtil.createPackBitsStream(imageInput, length);
|
||||
try {
|
||||
for (int x = 0; x < pChannelWidth; x++) {
|
||||
pRow[x] = input.readShort();
|
||||
@@ -466,7 +484,7 @@ public class PSDImageReader extends ImageReaderBase {
|
||||
}
|
||||
}
|
||||
else {
|
||||
mImageInput.readFully(pRow, 0, pChannelWidth);
|
||||
imageInput.readFully(pRow, 0, pChannelWidth);
|
||||
}
|
||||
|
||||
// TODO: Destination offset...??
|
||||
@@ -480,22 +498,22 @@ public class PSDImageReader extends ImageReaderBase {
|
||||
value = (short) (65535 - value & 0xffff);
|
||||
}
|
||||
|
||||
pData[offset + x * pBands] = value;
|
||||
pData.setElem(banded ? pChannel : 0, offset + x * pBands, value);
|
||||
}
|
||||
}
|
||||
else {
|
||||
mImageInput.skipBytes(length);
|
||||
imageInput.skipBytes(length);
|
||||
}
|
||||
|
||||
if (abortRequested()) {
|
||||
break;
|
||||
}
|
||||
processImageProgress((pChannel * y * 100) / pChannelCount * pChannelHeight);
|
||||
processImageProgressForChannel(pChannel, pChannelCount, y, pChannelHeight);
|
||||
}
|
||||
}
|
||||
|
||||
private void read8bitChannel(final int pChannel, final int pChannelCount,
|
||||
final byte[] pData, final int pBands, final int pBandOffset,
|
||||
final DataBuffer pData, final int pBands, final int pBandOffset,
|
||||
final ColorModel pSourceColorModel,
|
||||
final byte[] pRow,
|
||||
final Rectangle pSource, final Rectangle pDest,
|
||||
@@ -506,6 +524,7 @@ public class PSDImageReader extends ImageReaderBase {
|
||||
|
||||
final boolean isCMYK = pSourceColorModel.getColorSpace().getType() == ColorSpace.TYPE_CMYK;
|
||||
final int colorComponents = pSourceColorModel.getColorSpace().getNumComponents();
|
||||
final boolean banded = pData.getNumBanks() > 1;
|
||||
|
||||
for (int y = 0; y < pChannelHeight; y++) {
|
||||
int length = pRLECompressed ? pRowByteCounts[pRowOffset + y] : pChannelWidth;
|
||||
@@ -514,7 +533,7 @@ public class PSDImageReader extends ImageReaderBase {
|
||||
// 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);
|
||||
DataInputStream input = PSDUtil.createPackBitsStream(imageInput, length);
|
||||
try {
|
||||
input.readFully(pRow, 0, pChannelWidth);
|
||||
}
|
||||
@@ -523,7 +542,7 @@ public class PSDImageReader extends ImageReaderBase {
|
||||
}
|
||||
}
|
||||
else {
|
||||
mImageInput.readFully(pRow, 0, pChannelWidth);
|
||||
imageInput.readFully(pRow, 0, pChannelWidth);
|
||||
}
|
||||
|
||||
// TODO: If banded and not sub sampling/cmyk, we could just copy using System.arraycopy
|
||||
@@ -538,23 +557,23 @@ public class PSDImageReader extends ImageReaderBase {
|
||||
value = (byte) (255 - value & 0xff);
|
||||
}
|
||||
|
||||
pData[offset + x * pBands] = value;
|
||||
pData.setElem(banded ? pChannel : 0, offset + x * pBands, value);
|
||||
}
|
||||
}
|
||||
else {
|
||||
mImageInput.skipBytes(length);
|
||||
imageInput.skipBytes(length);
|
||||
}
|
||||
|
||||
if (abortRequested()) {
|
||||
break;
|
||||
}
|
||||
processImageProgress((pChannel * y * 100) / pChannelCount * pChannelHeight);
|
||||
processImageProgressForChannel(pChannel, pChannelCount, y, pChannelHeight);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings({"UnusedDeclaration"})
|
||||
private void read1bitChannel(final int pChannel, final int pChannelCount,
|
||||
final byte[] pData, final int pBands, final int pBandOffset,
|
||||
final DataBuffer pData, final int pBands, final int pBandOffset,
|
||||
final ColorModel pSourceColorModel,
|
||||
final byte[] pRow,
|
||||
final Rectangle pSource, final Rectangle pDest,
|
||||
@@ -564,6 +583,7 @@ public class PSDImageReader extends ImageReaderBase {
|
||||
// NOTE: 1 bit channels only occurs once
|
||||
|
||||
final int destWidth = (pDest.width + 7) / 8;
|
||||
final boolean banded = pData.getNumBanks() > 1;
|
||||
|
||||
for (int y = 0; y < pChannelHeight; y++) {
|
||||
int length = pRLECompressed ? pRowByteCounts[y] : pChannelWidth;
|
||||
@@ -572,7 +592,7 @@ public class PSDImageReader extends ImageReaderBase {
|
||||
// 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);
|
||||
DataInputStream input = PSDUtil.createPackBitsStream(imageInput, length);
|
||||
try {
|
||||
input.readFully(pRow, 0, pRow.length);
|
||||
}
|
||||
@@ -581,7 +601,7 @@ public class PSDImageReader extends ImageReaderBase {
|
||||
}
|
||||
}
|
||||
else {
|
||||
mImageInput.readFully(pRow, 0, pRow.length);
|
||||
imageInput.readFully(pRow, 0, pRow.length);
|
||||
}
|
||||
|
||||
// TODO: Destination offset...??
|
||||
@@ -591,7 +611,7 @@ public class PSDImageReader extends ImageReaderBase {
|
||||
for (int i = 0; i < destWidth; i++) {
|
||||
byte value = pRow[pSource.x / 8 + i * pXSub];
|
||||
// NOTE: Invert bits to match Java's default monochrome
|
||||
pData[offset + i] = (byte) (~value & 0xff);
|
||||
pData.setElem(banded ? pChannel : 0, offset + i, (byte) (~value & 0xff));
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -615,22 +635,22 @@ public class PSDImageReader extends ImageReaderBase {
|
||||
}
|
||||
|
||||
// NOTE: Invert bits to match Java's default monochrome
|
||||
pData[offset + i] = (byte) (~result & 0xff);
|
||||
pData.setElem(banded ? pChannel : 0, offset + i, (byte) (~result & 0xff));
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
mImageInput.skipBytes(length);
|
||||
imageInput.skipBytes(length);
|
||||
}
|
||||
|
||||
if (abortRequested()) {
|
||||
break;
|
||||
}
|
||||
processImageProgress((pChannel * y * 100) / pChannelCount * pChannelHeight);
|
||||
processImageProgressForChannel(pChannel, pChannelCount, y, pChannelHeight);
|
||||
}
|
||||
}
|
||||
|
||||
private void decomposeAlpha(final ColorModel pModel, final DataBufferByte pBuffer,
|
||||
private void decomposeAlpha(final ColorModel pModel, final DataBuffer pBuffer,
|
||||
final int pWidth, final int pHeight, final int pChannels) {
|
||||
// TODO: Is the document background always white!?
|
||||
// TODO: What about CMYK + alpha?
|
||||
@@ -638,48 +658,45 @@ public class PSDImageReader extends ImageReaderBase {
|
||||
|
||||
// TODO: Probably faster to do this in line..
|
||||
if (pBuffer.getNumBanks() > 1) {
|
||||
byte[][] data = pBuffer.getBankData();
|
||||
|
||||
for (int y = 0; y < pHeight; y++) {
|
||||
for (int x = 0; x < pWidth; x++) {
|
||||
int offset = (x + y * pWidth);
|
||||
// ARGB format
|
||||
int alpha = data[pChannels - 1][offset] & 0xff;
|
||||
int alpha = pBuffer.getElem(pChannels - 1, offset) & 0xff;
|
||||
|
||||
if (alpha != 0) {
|
||||
double normalizedAlpha = alpha / 255.0;
|
||||
|
||||
for (int i = 0; i < pChannels - 1; i++) {
|
||||
data[i][offset] = decompose(data[i][offset] & 0xff, normalizedAlpha);
|
||||
pBuffer.setElem(i, offset, decompose(pBuffer.getElem(i, offset) & 0xff, normalizedAlpha));
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (int i = 0; i < pChannels - 1; i++) {
|
||||
data[i][offset] = 0;
|
||||
pBuffer.setElem(i, offset, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
byte[] data = pBuffer.getData();
|
||||
|
||||
for (int y = 0; y < pHeight; y++) {
|
||||
for (int x = 0; x < pWidth; x++) {
|
||||
int offset = (x + y * pWidth) * pChannels;
|
||||
// ABGR format
|
||||
int alpha = data[offset] & 0xff;
|
||||
int alpha = pBuffer.getElem(offset) & 0xff;
|
||||
|
||||
if (alpha != 0) {
|
||||
double normalizedAlpha = alpha / 255.0;
|
||||
|
||||
for (int i = 1; i < pChannels; i++) {
|
||||
data[offset + i] = decompose(data[offset + i] & 0xff, normalizedAlpha);
|
||||
pBuffer.setElem(offset + i, decompose(pBuffer.getElem(offset + i) & 0xff, normalizedAlpha));
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (int i = 1; i < pChannels; i++) {
|
||||
data[offset + i] = 0;
|
||||
pBuffer.setElem(offset + i, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -697,7 +714,7 @@ public class PSDImageReader extends ImageReaderBase {
|
||||
private void readHeader() throws IOException {
|
||||
assertInput();
|
||||
if (mHeader == null) {
|
||||
mHeader = new PSDHeader(mImageInput);
|
||||
mHeader = new PSDHeader(imageInput);
|
||||
|
||||
mMetadata = new PSDMetadata();
|
||||
mMetadata.mHeader = mHeader;
|
||||
@@ -714,17 +731,17 @@ public class PSDImageReader extends ImageReaderBase {
|
||||
around as a black box for use when saving the file.
|
||||
*/
|
||||
if (mHeader.mMode == PSD.COLOR_MODE_INDEXED) {
|
||||
mMetadata.mColorData = new PSDColorData(mImageInput);
|
||||
mMetadata.mColorData = new PSDColorData(imageInput);
|
||||
}
|
||||
else {
|
||||
// TODO: We need to store the duotone spec if we decide to create a writer...
|
||||
// Skip color mode data for other modes
|
||||
long length = mImageInput.readUnsignedInt();
|
||||
mImageInput.skipBytes(length);
|
||||
long length = imageInput.readUnsignedInt();
|
||||
imageInput.skipBytes(length);
|
||||
}
|
||||
|
||||
// Don't need the header again
|
||||
mImageInput.flushBefore(mImageInput.getStreamPosition());
|
||||
imageInput.flushBefore(imageInput.getStreamPosition());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -732,40 +749,40 @@ public class PSDImageReader extends ImageReaderBase {
|
||||
// TODO: Obey ignoreMetadata
|
||||
private void readImageResources(final boolean pParseData) throws IOException {
|
||||
// TODO: Avoid unnecessary stream repositioning
|
||||
long pos = mImageInput.getFlushedPosition();
|
||||
mImageInput.seek(pos);
|
||||
long pos = imageInput.getFlushedPosition();
|
||||
imageInput.seek(pos);
|
||||
|
||||
long length = mImageInput.readUnsignedInt();
|
||||
long length = imageInput.readUnsignedInt();
|
||||
|
||||
if (pParseData && length > 0) {
|
||||
if (mMetadata.mImageResources == null) {
|
||||
mMetadata.mImageResources = new ArrayList<PSDImageResource>();
|
||||
long expectedEnd = mImageInput.getStreamPosition() + length;
|
||||
long expectedEnd = imageInput.getStreamPosition() + length;
|
||||
|
||||
while (mImageInput.getStreamPosition() < expectedEnd) {
|
||||
while (imageInput.getStreamPosition() < expectedEnd) {
|
||||
// TODO: Have PSDImageResources defer actual parsing? (Just store stream offsets)
|
||||
PSDImageResource resource = PSDImageResource.read(mImageInput);
|
||||
PSDImageResource resource = PSDImageResource.read(imageInput);
|
||||
mMetadata.mImageResources.add(resource);
|
||||
}
|
||||
|
||||
if (mImageInput.getStreamPosition() != expectedEnd) {
|
||||
if (imageInput.getStreamPosition() != expectedEnd) {
|
||||
throw new IIOException("Corrupt PSD document"); // ..or maybe just a bug in the reader.. ;-)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mImageInput.seek(pos + length + 4);
|
||||
imageInput.seek(pos + length + 4);
|
||||
}
|
||||
|
||||
// TODO: Flags or list of interesting resources to parse
|
||||
// TODO: Obey ignoreMetadata
|
||||
private void readLayerAndMaskInfo(final boolean pParseData) throws IOException {
|
||||
// TODO: Make sure we are positioned correctly
|
||||
long length = mImageInput.readUnsignedInt();
|
||||
long length = imageInput.readUnsignedInt();
|
||||
if (pParseData && length > 0) {
|
||||
long pos = mImageInput.getStreamPosition();
|
||||
long pos = imageInput.getStreamPosition();
|
||||
|
||||
long layerInfoLength = mImageInput.readUnsignedInt();
|
||||
long layerInfoLength = imageInput.readUnsignedInt();
|
||||
|
||||
/*
|
||||
"Layer count. If it is a negative number, its absolute value is the number of
|
||||
@@ -773,19 +790,19 @@ public class PSDImageReader extends ImageReaderBase {
|
||||
merged result."
|
||||
*/
|
||||
// TODO: Figure out what the last part of that sentence means in practice...
|
||||
int layers = mImageInput.readShort();
|
||||
int layers = imageInput.readShort();
|
||||
|
||||
PSDLayerInfo[] layerInfos = new PSDLayerInfo[Math.abs(layers)];
|
||||
for (int i = 0; i < layerInfos.length; i++) {
|
||||
layerInfos[i] = new PSDLayerInfo(mImageInput);
|
||||
layerInfos[i] = new PSDLayerInfo(imageInput);
|
||||
}
|
||||
mMetadata.mLayerInfo = Arrays.asList(layerInfos);
|
||||
|
||||
// TODO: Clean-up
|
||||
mImageInput.mark();
|
||||
imageInput.mark();
|
||||
ImageTypeSpecifier raw = getRawImageTypeInternal(0);
|
||||
ImageTypeSpecifier imageType = getImageTypes(0).next();
|
||||
mImageInput.reset();
|
||||
imageInput.reset();
|
||||
|
||||
for (PSDLayerInfo layerInfo : layerInfos) {
|
||||
// TODO: If not explicitly needed, skip layers...
|
||||
@@ -797,30 +814,30 @@ public class PSDImageReader extends ImageReaderBase {
|
||||
// }
|
||||
}
|
||||
|
||||
long read = mImageInput.getStreamPosition() - pos;
|
||||
long read = imageInput.getStreamPosition() - pos;
|
||||
|
||||
long diff = layerInfoLength - (read - 4); // - 4 for the layerInfoLength field itself
|
||||
// System.out.println("diff: " + diff);
|
||||
mImageInput.skipBytes(diff);
|
||||
imageInput.skipBytes(diff);
|
||||
|
||||
// TODO: Global LayerMaskInfo (18 bytes or more..?)
|
||||
// 4 (length), 2 (colorSpace), 8 (4 * 2 byte color components), 2 (opacity %), 1 (kind), variable (pad)
|
||||
long layerMaskInfoLength = mImageInput.readUnsignedInt();
|
||||
long layerMaskInfoLength = imageInput.readUnsignedInt();
|
||||
// System.out.println("GlobalLayerMaskInfo length: " + layerMaskInfoLength);
|
||||
if (layerMaskInfoLength > 0) {
|
||||
mMetadata.mGlobalLayerMask = new PSDGlobalLayerMask(mImageInput);
|
||||
mMetadata.mGlobalLayerMask = new PSDGlobalLayerMask(imageInput);
|
||||
// System.out.println("mGlobalLayerMask: " + mGlobalLayerMask);
|
||||
}
|
||||
|
||||
read = mImageInput.getStreamPosition() - pos;
|
||||
read = imageInput.getStreamPosition() - pos;
|
||||
|
||||
long toSkip = length - read;
|
||||
// System.out.println("toSkip: " + toSkip);
|
||||
mImageInput.skipBytes(toSkip);
|
||||
imageInput.skipBytes(toSkip);
|
||||
}
|
||||
else {
|
||||
// Skip entire layer and mask section
|
||||
mImageInput.skipBytes(length);
|
||||
imageInput.skipBytes(length);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -853,13 +870,13 @@ public class PSDImageReader extends ImageReaderBase {
|
||||
final int interleavedBands = banded ? 1 : raster.getNumBands();
|
||||
|
||||
for (PSDChannelInfo channelInfo : pLayerInfo.mChannelInfo) {
|
||||
int compression = mImageInput.readUnsignedShort();
|
||||
int compression = imageInput.readUnsignedShort();
|
||||
|
||||
// Skip layer if we can't read it
|
||||
// channelId == -2 means "user supplied layer mask", whatever that is...
|
||||
if (width <= 0 || height <= 0 || channelInfo.mChannelId == -2 ||
|
||||
(compression != PSD.COMPRESSION_NONE && compression != PSD.COMPRESSION_RLE)) {
|
||||
mImageInput.skipBytes(channelInfo.mLength - 2);
|
||||
imageInput.skipBytes(channelInfo.mLength - 2);
|
||||
}
|
||||
else {
|
||||
// 0 = red, 1 = green, etc
|
||||
@@ -881,7 +898,7 @@ public class PSDImageReader extends ImageReaderBase {
|
||||
// each count stored as a two*byte value.
|
||||
byteCounts = new int[pLayerInfo.mBottom - pLayerInfo.mTop];
|
||||
for (int i = 0; i < byteCounts.length; i++) {
|
||||
byteCounts[i] = mImageInput.readUnsignedShort();
|
||||
byteCounts[i] = imageInput.readUnsignedShort();
|
||||
}
|
||||
|
||||
break;
|
||||
@@ -897,24 +914,25 @@ public class PSDImageReader extends ImageReaderBase {
|
||||
switch (mHeader.mBits) {
|
||||
case 1:
|
||||
byte[] row1 = ((DataBufferByte) rowRaster.getDataBuffer()).getData();
|
||||
DataBufferByte buffer1 = (DataBufferByte) raster.getDataBuffer();
|
||||
byte[] data1 = banded ? buffer1.getData(c) : buffer1.getData();
|
||||
// DataBufferByte buffer1 = (DataBufferByte) raster.getDataBuffer();
|
||||
// byte[] data1 = banded ? buffer1.getData(c) : buffer1.getData();
|
||||
|
||||
read1bitChannel(c, imageType.getNumBands(), data1, interleavedBands, bandOffset, sourceCM, row1, area, area, xsub, ysub, width, height, byteCounts, compression == PSD.COMPRESSION_RLE);
|
||||
read1bitChannel(c, imageType.getNumBands(), raster.getDataBuffer(), interleavedBands, bandOffset, sourceCM, row1, area, area, xsub, ysub, width, height, byteCounts, 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();
|
||||
// DataBufferByte buffer8 = (DataBufferByte) raster.getDataBuffer();
|
||||
// byte[] data8 = banded ? buffer8.getData(c) : buffer8.getData();
|
||||
|
||||
read8bitChannel(c, imageType.getNumBands(), data8, interleavedBands, bandOffset, sourceCM, row8, area, area, xsub, ysub, width, height, byteCounts, 0, compression == PSD.COMPRESSION_RLE);
|
||||
// read8bitChannel(c, imageType.getNumBands(), data8, interleavedBands, bandOffset, sourceCM, row8, area, area, xsub, ysub, width, height, byteCounts, 0, compression == PSD.COMPRESSION_RLE);
|
||||
read8bitChannel(c, imageType.getNumBands(), raster.getDataBuffer(), interleavedBands, bandOffset, sourceCM, row8, area, area, xsub, ysub, width, height, byteCounts, 0, 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();
|
||||
// DataBufferUShort buffer16 = (DataBufferUShort) raster.getDataBuffer();
|
||||
// short[] data16 = banded ? buffer16.getData(c) : buffer16.getData();
|
||||
|
||||
read16bitChannel(c, imageType.getNumBands(), data16, interleavedBands, bandOffset, sourceCM, row16, area, area, xsub, ysub, width, height, byteCounts, 0, compression == PSD.COMPRESSION_RLE);
|
||||
read16bitChannel(c, imageType.getNumBands(), raster.getDataBuffer(), interleavedBands, bandOffset, sourceCM, row16, area, area, xsub, ysub, width, height, byteCounts, 0, compression == PSD.COMPRESSION_RLE);
|
||||
break;
|
||||
default:
|
||||
throw new IIOException(String.format("Unknown PSD bit depth: %s", mHeader.mBits));
|
||||
@@ -995,7 +1013,7 @@ public class PSDImageReader extends ImageReaderBase {
|
||||
readLayerAndMaskInfo(true);
|
||||
|
||||
// TODO: Need to make sure compression is set in metadata, even without reading the image data!
|
||||
mMetadata.mCompression = mImageInput.readShort();
|
||||
mMetadata.mCompression = imageInput.readShort();
|
||||
|
||||
// mMetadata.mHeader = mHeader;
|
||||
// mMetadata.mColorData = mColorData;
|
||||
|
||||
+1
-1
@@ -43,7 +43,7 @@ import java.lang.reflect.Field;
|
||||
* @author last modified by $Author: haraldk$
|
||||
* @version $Id: PSDImageResource.java,v 1.0 Apr 29, 2008 5:49:06 PM haraldk Exp$
|
||||
*/
|
||||
class PSDImageResource {
|
||||
public class PSDImageResource {
|
||||
// TODO: Refactor image resources to separate package
|
||||
// TODO: Change constructor to store stream offset and length only (+ possibly the name), defer reading
|
||||
|
||||
|
||||
-122
@@ -1,122 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name "TwelveMonkeys" nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.imageio.plugins.psd;
|
||||
|
||||
import java.awt.color.ColorSpace;
|
||||
|
||||
/**
|
||||
* YCbCrColorSpace
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @author last modified by $Author: haraldk$
|
||||
* @version $Id: YCbCrColorSpace.java,v 1.0 Jun 28, 2008 3:30:50 PM haraldk Exp$
|
||||
*/
|
||||
// TODO: Move to com.twlevemonkeys.image?
|
||||
// TODO: Read an ICC YCbCr profile from classpath resource? Is there such a thing?
|
||||
final class YCbCrColorSpace extends ColorSpace {
|
||||
|
||||
static final ColorSpace INSTANCE = new CMYKColorSpace();
|
||||
final ColorSpace sRGB = getInstance(CS_sRGB);
|
||||
|
||||
YCbCrColorSpace() {
|
||||
super(ColorSpace.TYPE_YCbCr, 3);
|
||||
}
|
||||
|
||||
public static ColorSpace getInstance() {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
// http://www.w3.org/Graphics/JPEG/jfif.txt
|
||||
/*
|
||||
Conversion to and from RGB
|
||||
|
||||
Y, Cb, and Cr are converted from R, G, and B as defined in CCIR Recommendation 601
|
||||
but are normalized so as to occupy the full 256 levels of a 8-bit binary encoding. More
|
||||
precisely:
|
||||
|
||||
Y = 256 * E'y
|
||||
Cb = 256 * [ E'Cb ] + 128
|
||||
Cr = 256 * [ E'Cr ] + 128
|
||||
|
||||
where the E'y, E'Cb and E'Cb are defined as in CCIR 601. Since values of E'y have a
|
||||
range of 0 to 1.0 and those for E'Cb and E'Cr have a range of -0.5 to +0.5, Y, Cb, and Cr
|
||||
must be clamped to 255 when they are maximum value.
|
||||
|
||||
RGB to YCbCr Conversion
|
||||
|
||||
YCbCr (256 levels) can be computed directly from 8-bit RGB as follows:
|
||||
|
||||
Y = 0.299 R + 0.587 G + 0.114 B
|
||||
Cb = - 0.1687 R - 0.3313 G + 0.5 B + 128
|
||||
Cr = 0.5 R - 0.4187 G - 0.0813 B + 128
|
||||
|
||||
NOTE - Not all image file formats store image samples in the order R0, G0,
|
||||
B0, ... Rn, Gn, Bn. Be sure to verify the sample order before converting an
|
||||
RGB file to JFIF.
|
||||
|
||||
YCbCr to RGB Conversion
|
||||
|
||||
RGB can be computed directly from YCbCr (256 levels) as follows:
|
||||
|
||||
R = Y + 1.402 (Cr-128)
|
||||
G = Y - 0.34414 (Cb-128) - 0.71414 (Cr-128)
|
||||
B = Y + 1.772 (Cb-128)
|
||||
*/
|
||||
public float[] toRGB(float[] colorvalue) {
|
||||
// R = Y + 1.402 (Cr-128)
|
||||
// G = Y - 0.34414 (Cb-128) - 0.71414 (Cr-128)
|
||||
// B = Y + 1.772 (Cb-128)
|
||||
return new float[] {
|
||||
colorvalue[0] + 1.402f * (colorvalue[2] - .5f),
|
||||
colorvalue[0] - 0.34414f * (colorvalue[1] - .5f) - 0.71414f * (colorvalue[2] - .5f),
|
||||
colorvalue[0] + 1.772f * (colorvalue[1] - .5f),
|
||||
};
|
||||
// TODO: Convert via CIEXYZ space using sRGB space, as suggested in docs
|
||||
// return sRGB.fromCIEXYZ(toCIEXYZ(colorvalue));
|
||||
}
|
||||
|
||||
public float[] fromRGB(float[] rgbvalue) {
|
||||
// Y = 0.299 R + 0.587 G + 0.114 B
|
||||
// Cb = - 0.1687 R - 0.3313 G + 0.5 B + 128
|
||||
// Cr = 0.5 R - 0.4187 G - 0.0813 B + 128
|
||||
return new float[] {
|
||||
0.299f * rgbvalue[0] + 0.587f * rgbvalue[1] + 0.114f * rgbvalue[2],
|
||||
-0.1687f * rgbvalue[0] - 0.3313f * rgbvalue[1] + 0.5f * rgbvalue[2] + .5f,
|
||||
0.5f * rgbvalue[0] - 0.4187f * rgbvalue[1] - 0.0813f * rgbvalue[2] + .5f
|
||||
};
|
||||
}
|
||||
|
||||
public float[] toCIEXYZ(float[] colorvalue) {
|
||||
throw new UnsupportedOperationException("Method toCIEXYZ not implemented"); // TODO: Implement
|
||||
}
|
||||
|
||||
public float[] fromCIEXYZ(float[] colorvalue) {
|
||||
throw new UnsupportedOperationException("Method fromCIEXYZ not implemented"); // TODO: Implement
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user