mirror of
https://github.com/haraldk/TwelveMonkeys.git
synced 2025-10-03 23:53:15 -04:00
Mainly new code standard.
A few changes that should have been committed earlier.. :-/
This commit is contained in:
@@ -98,8 +98,8 @@ public class SVGImageReader extends ImageReaderBase {
|
||||
public void setInput(Object pInput, boolean pSeekForwardOnly, boolean pIgnoreMetadata) {
|
||||
super.setInput(pInput, pSeekForwardOnly, pIgnoreMetadata);
|
||||
|
||||
if (mImageInput != null) {
|
||||
TranscoderInput input = new TranscoderInput(IIOUtil.createStreamAdapter(mImageInput));
|
||||
if (imageInput != null) {
|
||||
TranscoderInput input = new TranscoderInput(IIOUtil.createStreamAdapter(imageInput));
|
||||
mRasterizer.setInput(input);
|
||||
}
|
||||
}
|
||||
|
@@ -137,25 +137,25 @@ public class TIFFImageReader extends ImageReaderBase {
|
||||
|
||||
private synchronized void init() {
|
||||
if (mDecoder == null) {
|
||||
if (mImageInput == null) {
|
||||
if (imageInput == null) {
|
||||
throw new IllegalStateException("input == null");
|
||||
}
|
||||
|
||||
mDecoder = new TIFFImageDecoder(new SeekableStream() {
|
||||
public int read() throws IOException {
|
||||
return mImageInput.read();
|
||||
return imageInput.read();
|
||||
}
|
||||
|
||||
public int read(final byte[] pBytes, final int pStart, final int pLength) throws IOException {
|
||||
return mImageInput.read(pBytes, pStart, pLength);
|
||||
return imageInput.read(pBytes, pStart, pLength);
|
||||
}
|
||||
|
||||
public long getFilePointer() throws IOException {
|
||||
return mImageInput.getStreamPosition();
|
||||
return imageInput.getStreamPosition();
|
||||
}
|
||||
|
||||
public void seek(final long pPos) throws IOException {
|
||||
mImageInput.seek(pPos);
|
||||
imageInput.seek(pPos);
|
||||
}
|
||||
}, null);
|
||||
}
|
||||
|
@@ -124,7 +124,7 @@ public class TIFFImageWriter extends ImageWriterBase {
|
||||
processImageStarted(0);
|
||||
|
||||
mEncoder.encode(image);
|
||||
mImageOutput.flush();
|
||||
imageOutput.flush();
|
||||
|
||||
processImageComplete();
|
||||
}
|
||||
@@ -136,10 +136,10 @@ public class TIFFImageWriter extends ImageWriterBase {
|
||||
|
||||
private synchronized void init() {
|
||||
if (mEncoder == null) {
|
||||
if (mImageOutput == null) {
|
||||
if (imageOutput == null) {
|
||||
throw new IllegalStateException("output == null");
|
||||
}
|
||||
mEncoder = new TIFFImageEncoder(IIOUtil.createStreamAdapter(mImageOutput), null);
|
||||
mEncoder = new TIFFImageEncoder(IIOUtil.createStreamAdapter(imageOutput), null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -88,7 +88,7 @@ public class WMFImageReader extends ImageReaderBase {
|
||||
|
||||
private synchronized void init() throws IOException {
|
||||
// Need the extra test, to avoid throwing an IOException from the Transcoder
|
||||
if (mImageInput == null) {
|
||||
if (imageInput == null) {
|
||||
throw new IllegalStateException("input == null");
|
||||
}
|
||||
|
||||
@@ -98,7 +98,7 @@ public class WMFImageReader extends ImageReaderBase {
|
||||
ByteArrayOutputStream output = new ByteArrayOutputStream();
|
||||
Writer writer = new OutputStreamWriter(output, "UTF8");
|
||||
try {
|
||||
TranscoderInput in = new TranscoderInput(IIOUtil.createStreamAdapter(mImageInput));
|
||||
TranscoderInput in = new TranscoderInput(IIOUtil.createStreamAdapter(imageInput));
|
||||
TranscoderOutput out = new TranscoderOutput(writer);
|
||||
|
||||
// TODO: Transcodinghints?
|
||||
|
@@ -37,9 +37,7 @@ import javax.imageio.spi.ImageReaderSpi;
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.awt.event.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.awt.image.IndexColorModel;
|
||||
import java.io.File;
|
||||
@@ -61,7 +59,7 @@ public abstract class ImageReaderBase extends ImageReader {
|
||||
* For convenience. Only set if the input is an {@code ImageInputStream}.
|
||||
* @see #setInput(Object, boolean, boolean)
|
||||
*/
|
||||
protected ImageInputStream mImageInput;
|
||||
protected ImageInputStream imageInput;
|
||||
|
||||
/**
|
||||
* Constructs an {@code ImageReader} and sets its
|
||||
@@ -102,7 +100,7 @@ public abstract class ImageReaderBase extends ImageReader {
|
||||
resetMembers();
|
||||
super.setInput(pInput, pSeekForwardOnly, pIgnoreMetadata);
|
||||
if (pInput instanceof ImageInputStream) {
|
||||
mImageInput = (ImageInputStream) pInput;
|
||||
imageInput = (ImageInputStream) pInput;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -229,7 +227,7 @@ public abstract class ImageReaderBase extends ImageReader {
|
||||
if (dest != null) {
|
||||
boolean found = false;
|
||||
|
||||
// NOTE: This is bad, as it relies on implementation details of super method...
|
||||
// NOTE: This is bad, as it relies on implementation details of "super" method...
|
||||
// We know that the iterator has not been touched if explicit destination..
|
||||
while (pTypes.hasNext()) {
|
||||
ImageTypeSpecifier specifier = pTypes.next();
|
||||
@@ -318,7 +316,27 @@ public abstract class ImageReaderBase extends ImageReader {
|
||||
SwingUtilities.invokeAndWait(new Runnable() {
|
||||
public void run() {
|
||||
JFrame frame = new JFrame(pTitle);
|
||||
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
||||
|
||||
frame.getRootPane().getActionMap().put("window-close", new AbstractAction() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
Window window = SwingUtilities.getWindowAncestor((Component) e.getSource());
|
||||
window.setVisible(false);
|
||||
window.dispose();
|
||||
}
|
||||
});
|
||||
frame.getRootPane().getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_W, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()), "window-close");
|
||||
|
||||
frame.addWindowListener(new WindowAdapter() {
|
||||
@Override
|
||||
public void windowClosed(WindowEvent e) {
|
||||
Window[] windows = Window.getWindows();
|
||||
if (windows == null || windows.length == 0) {
|
||||
System.exit(0);
|
||||
}
|
||||
}
|
||||
});
|
||||
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
|
||||
|
||||
frame.setLocationByPlatform(true);
|
||||
JPanel pane = new JPanel(new BorderLayout());
|
||||
JScrollPane scroll = new JScrollPane(new ImageLabel(pImage));
|
||||
@@ -338,6 +356,10 @@ public abstract class ImageReaderBase extends ImageReader {
|
||||
}
|
||||
}
|
||||
|
||||
protected static boolean hasExplicitDestination(final ImageReadParam pParam) {
|
||||
return (pParam != null && (pParam.getDestination() != null || pParam.getDestinationType() != null || pParam.getDestinationOffset() != null));
|
||||
}
|
||||
|
||||
private static class ImageLabel extends JLabel {
|
||||
Paint mBackground;
|
||||
|
||||
|
@@ -52,7 +52,7 @@ public abstract class ImageWriterBase extends ImageWriter {
|
||||
* For convenience. Only set if the output is an {@code ImageInputStream}.
|
||||
* @see #setOutput(Object)
|
||||
*/
|
||||
protected ImageOutputStream mImageOutput;
|
||||
protected ImageOutputStream imageOutput;
|
||||
|
||||
/**
|
||||
* Constructs an {@code ImageWriter} and sets its
|
||||
@@ -80,7 +80,7 @@ public abstract class ImageWriterBase extends ImageWriter {
|
||||
super.setOutput(pOutput);
|
||||
|
||||
if (pOutput instanceof ImageOutputStream) {
|
||||
mImageOutput = (ImageOutputStream) pOutput;
|
||||
imageOutput = (ImageOutputStream) pOutput;
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -16,9 +16,9 @@ public class ProviderInfo {
|
||||
// TODO: Consider reading the META-INF/MANIFEST.MF from the class path using java.util.jar.Manifest.
|
||||
// Use the manifest that is located in the same class path folder as the package.
|
||||
|
||||
private final String mTitle;
|
||||
private final String mVendorName;
|
||||
private final String mVersion;
|
||||
private final String title;
|
||||
private final String vendorName;
|
||||
private final String version;
|
||||
|
||||
/**
|
||||
* Creates a provider information instance based on the given package.
|
||||
@@ -32,13 +32,13 @@ public class ProviderInfo {
|
||||
Validate.notNull(pPackage, "package");
|
||||
|
||||
String title = pPackage.getImplementationTitle();
|
||||
mTitle = title != null ? title : pPackage.getName();
|
||||
this.title = title != null ? title : pPackage.getName();
|
||||
|
||||
String vendor = pPackage.getImplementationVendor();
|
||||
mVendorName = vendor != null ? vendor : fakeVendor(pPackage);
|
||||
vendorName = vendor != null ? vendor : fakeVendor(pPackage);
|
||||
|
||||
String version = pPackage.getImplementationVersion();
|
||||
mVersion = version != null ? version : fakeVersion(pPackage);
|
||||
this.version = version != null ? version : fakeVersion(pPackage);
|
||||
}
|
||||
|
||||
private static String fakeVendor(final Package pPackage) {
|
||||
@@ -60,7 +60,7 @@ public class ProviderInfo {
|
||||
* @return the implementation title
|
||||
*/
|
||||
final String getImplementationTitle() {
|
||||
return mTitle;
|
||||
return title;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -72,7 +72,7 @@ public class ProviderInfo {
|
||||
* @return the vendor name.
|
||||
*/
|
||||
public final String getVendorName() {
|
||||
return mVendorName;
|
||||
return vendorName;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -83,11 +83,11 @@ public class ProviderInfo {
|
||||
* @return the vendor name.
|
||||
*/
|
||||
public final String getVersion() {
|
||||
return mVersion;
|
||||
return version;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return mTitle + ", " + mVersion + " by " + mVendorName;
|
||||
return title + ", " + version + " by " + vendorName;
|
||||
}
|
||||
}
|
||||
|
@@ -23,12 +23,12 @@ public final class BufferedImageInputStream extends ImageInputStreamImpl impleme
|
||||
|
||||
static final int DEFAULT_BUFFER_SIZE = 8192;
|
||||
|
||||
private ImageInputStream mStream;
|
||||
private ImageInputStream stream;
|
||||
|
||||
private byte[] mBuffer;
|
||||
private long mBufferStart = 0;
|
||||
private int mBufferPos = 0;
|
||||
private int mBufferLength = 0;
|
||||
private byte[] buffer;
|
||||
private long bufferStart = 0;
|
||||
private int bufferPos = 0;
|
||||
private int bufferLength = 0;
|
||||
|
||||
public BufferedImageInputStream(final ImageInputStream pStream) throws IOException {
|
||||
this(pStream, DEFAULT_BUFFER_SIZE);
|
||||
@@ -37,19 +37,19 @@ public final class BufferedImageInputStream extends ImageInputStreamImpl impleme
|
||||
private BufferedImageInputStream(final ImageInputStream pStream, final int pBufferSize) throws IOException {
|
||||
Validate.notNull(pStream, "stream");
|
||||
|
||||
mStream = pStream;
|
||||
stream = pStream;
|
||||
streamPos = pStream.getStreamPosition();
|
||||
mBuffer = new byte[pBufferSize];
|
||||
buffer = new byte[pBufferSize];
|
||||
}
|
||||
|
||||
private void fillBuffer() throws IOException {
|
||||
mBufferStart = streamPos;
|
||||
mBufferLength = mStream.read(mBuffer, 0, mBuffer.length);
|
||||
mBufferPos = 0;
|
||||
bufferStart = streamPos;
|
||||
bufferLength = stream.read(buffer, 0, buffer.length);
|
||||
bufferPos = 0;
|
||||
}
|
||||
|
||||
private boolean isBufferValid() throws IOException {
|
||||
return mBufferPos < mBufferLength && mBufferStart == mStream.getStreamPosition() - mBufferLength;
|
||||
return bufferPos < bufferLength && bufferStart == stream.getStreamPosition() - bufferLength;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -58,14 +58,14 @@ public final class BufferedImageInputStream extends ImageInputStreamImpl impleme
|
||||
fillBuffer();
|
||||
}
|
||||
|
||||
if (mBufferLength <= 0) {
|
||||
if (bufferLength <= 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
bitOffset = 0;
|
||||
streamPos++;
|
||||
|
||||
return mBuffer[mBufferPos++] & 0xff;
|
||||
return buffer[bufferPos++] & 0xff;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -74,7 +74,7 @@ public final class BufferedImageInputStream extends ImageInputStreamImpl impleme
|
||||
|
||||
if (!isBufferValid()) {
|
||||
// Bypass cache if cache is empty for reads longer than buffer
|
||||
if (pLength >= mBuffer.length) {
|
||||
if (pLength >= buffer.length) {
|
||||
return readDirect(pBuffer, pOffset, pLength);
|
||||
}
|
||||
else {
|
||||
@@ -87,30 +87,30 @@ public final class BufferedImageInputStream extends ImageInputStreamImpl impleme
|
||||
|
||||
private int readDirect(final byte[] pBuffer, final int pOffset, final int pLength) throws IOException {
|
||||
// TODO: Figure out why reading more than the buffer length causes alignment issues...
|
||||
int read = mStream.read(pBuffer, pOffset, Math.min(mBuffer.length, pLength));
|
||||
int read = stream.read(pBuffer, pOffset, Math.min(buffer.length, pLength));
|
||||
|
||||
if (read > 0) {
|
||||
streamPos += read;
|
||||
}
|
||||
|
||||
mBufferStart = mStream.getStreamPosition();
|
||||
mBufferLength = 0;
|
||||
bufferStart = stream.getStreamPosition();
|
||||
bufferLength = 0;
|
||||
|
||||
return read;
|
||||
}
|
||||
|
||||
|
||||
private int readBuffered(final byte[] pBuffer, final int pOffset, final int pLength) {
|
||||
if (mBufferLength <= 0) {
|
||||
if (bufferLength <= 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Read as much as possible from buffer
|
||||
int length = Math.min(mBufferLength - mBufferPos, pLength);
|
||||
int length = Math.min(bufferLength - bufferPos, pLength);
|
||||
|
||||
if (length > 0) {
|
||||
System.arraycopy(mBuffer, mBufferPos, pBuffer, pOffset, length);
|
||||
mBufferPos += length;
|
||||
System.arraycopy(buffer, bufferPos, pBuffer, pOffset, length);
|
||||
bufferPos += length;
|
||||
}
|
||||
|
||||
streamPos += length;
|
||||
@@ -121,42 +121,42 @@ public final class BufferedImageInputStream extends ImageInputStreamImpl impleme
|
||||
@Override
|
||||
public void seek(long pPosition) throws IOException {
|
||||
// TODO: Could probably be optimized to not invalidate buffer if new position is within current buffer
|
||||
mStream.seek(pPosition);
|
||||
mBufferLength = 0; // Will invalidate buffer
|
||||
streamPos = mStream.getStreamPosition();
|
||||
stream.seek(pPosition);
|
||||
bufferLength = 0; // Will invalidate buffer
|
||||
streamPos = stream.getStreamPosition();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flushBefore(long pos) throws IOException {
|
||||
mStream.flushBefore(pos);
|
||||
stream.flushBefore(pos);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getFlushedPosition() {
|
||||
return mStream.getFlushedPosition();
|
||||
return stream.getFlushedPosition();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCached() {
|
||||
return mStream.isCached();
|
||||
return stream.isCached();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCachedMemory() {
|
||||
return mStream.isCachedMemory();
|
||||
return stream.isCachedMemory();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCachedFile() {
|
||||
return mStream.isCachedFile();
|
||||
return stream.isCachedFile();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
if (mStream != null) {
|
||||
//mStream.close();
|
||||
mStream = null;
|
||||
mBuffer = null;
|
||||
if (stream != null) {
|
||||
//stream.close();
|
||||
stream = null;
|
||||
buffer = null;
|
||||
}
|
||||
super.close();
|
||||
}
|
||||
@@ -170,7 +170,7 @@ public final class BufferedImageInputStream extends ImageInputStreamImpl impleme
|
||||
public long length() {
|
||||
// WTF?! This method is allowed to throw IOException in the interface...
|
||||
try {
|
||||
return mStream.length();
|
||||
return stream.length();
|
||||
}
|
||||
catch (IOException ignore) {
|
||||
}
|
||||
|
@@ -13,35 +13,39 @@ import java.io.IOException;
|
||||
* @version $Id: ByteArrayImageInputStream.java,v 1.0 May 15, 2008 2:12:12 PM haraldk Exp$
|
||||
*/
|
||||
public final class ByteArrayImageInputStream extends ImageInputStreamImpl {
|
||||
private final byte[] mData;
|
||||
private final byte[] data;
|
||||
|
||||
public ByteArrayImageInputStream(final byte[] pData) {
|
||||
Validate.notNull(pData, "data");
|
||||
mData = pData;
|
||||
data = pData;
|
||||
}
|
||||
|
||||
public int read() throws IOException {
|
||||
if (streamPos >= mData.length) {
|
||||
if (streamPos >= data.length) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
bitOffset = 0;
|
||||
return mData[((int) streamPos++)] & 0xff;
|
||||
|
||||
return data[((int) streamPos++)] & 0xff;
|
||||
}
|
||||
|
||||
public int read(byte[] pBuffer, int pOffset, int pLength) throws IOException {
|
||||
if (streamPos >= mData.length) {
|
||||
if (streamPos >= data.length) {
|
||||
return -1;
|
||||
}
|
||||
int length = (int) Math.min(mData.length - streamPos, pLength);
|
||||
|
||||
int length = (int) Math.min(data.length - streamPos, pLength);
|
||||
bitOffset = 0;
|
||||
System.arraycopy(mData, (int) streamPos, pBuffer, pOffset, length);
|
||||
System.arraycopy(data, (int) streamPos, pBuffer, pOffset, length);
|
||||
streamPos += length;
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long length() {
|
||||
return mData.length;
|
||||
return data.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@@ -16,9 +16,9 @@ import java.io.IOException;
|
||||
public final class SubImageInputStream extends ImageInputStreamImpl {
|
||||
// NOTE: This class is based on com.sun.imageio.plugins.common.SubImageInputStream, but fixes some of its bugs.
|
||||
|
||||
private final ImageInputStream mStream;
|
||||
private final long mStartPos;
|
||||
private final long mLength;
|
||||
private final ImageInputStream stream;
|
||||
private final long startPos;
|
||||
private final long length;
|
||||
|
||||
/**
|
||||
* Creates a {@link ImageInputStream}, reading up to a maximum number of bytes from the underlying stream.
|
||||
@@ -32,21 +32,19 @@ public final class SubImageInputStream extends ImageInputStreamImpl {
|
||||
*/
|
||||
public SubImageInputStream(final ImageInputStream pStream, final long pLength) throws IOException {
|
||||
Validate.notNull(pStream, "stream");
|
||||
if (pLength < 0) {
|
||||
throw new IllegalArgumentException("length < 0");
|
||||
}
|
||||
Validate.isTrue(pLength >= 0, pLength, "length < 0: %d");
|
||||
|
||||
mStream = pStream;
|
||||
mStartPos = pStream.getStreamPosition();
|
||||
mLength = pLength;
|
||||
stream = pStream;
|
||||
startPos = pStream.getStreamPosition();
|
||||
length = pLength;
|
||||
}
|
||||
|
||||
public int read() throws IOException {
|
||||
if (streamPos >= mLength) { // Local EOF
|
||||
if (streamPos >= length) { // Local EOF
|
||||
return -1;
|
||||
}
|
||||
else {
|
||||
int read = mStream.read();
|
||||
int read = stream.read();
|
||||
|
||||
if (read >= 0) {
|
||||
streamPos++;
|
||||
@@ -57,13 +55,13 @@ public final class SubImageInputStream extends ImageInputStreamImpl {
|
||||
}
|
||||
|
||||
public int read(final byte[] pBytes, final int pOffset, final int pLength) throws IOException {
|
||||
if (streamPos >= mLength) { // Local EOF
|
||||
if (streamPos >= length) { // Local EOF
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Safe cast, as pLength can never cause int overflow
|
||||
int length = (int) Math.min(pLength, mLength - streamPos);
|
||||
int count = mStream.read(pBytes, pOffset, length);
|
||||
int length = (int) Math.min(pLength, this.length - streamPos);
|
||||
int count = stream.read(pBytes, pOffset, length);
|
||||
|
||||
if (count >= 0) {
|
||||
streamPos += count;
|
||||
@@ -75,8 +73,8 @@ public final class SubImageInputStream extends ImageInputStreamImpl {
|
||||
@Override
|
||||
public long length() {
|
||||
try {
|
||||
long length = mStream.length();
|
||||
return length < 0 ? -1 : Math.min(length - mStartPos, mLength);
|
||||
long length = stream.length();
|
||||
return length < 0 ? -1 : Math.min(length - startPos, this.length);
|
||||
}
|
||||
catch (IOException ignore) {
|
||||
}
|
||||
@@ -90,7 +88,7 @@ public final class SubImageInputStream extends ImageInputStreamImpl {
|
||||
throw new IndexOutOfBoundsException("pos < flushedPosition");
|
||||
}
|
||||
|
||||
mStream.seek(mStartPos + pPosition);
|
||||
stream.seek(startPos + pPosition);
|
||||
streamPos = pPosition;
|
||||
}
|
||||
|
||||
|
@@ -43,10 +43,10 @@ import java.io.InputStream;
|
||||
* @version $Id: IIOInputStreamAdapter.java,v 1.0 Sep 26, 2007 11:35:59 AM haraldk Exp$
|
||||
*/
|
||||
class IIOInputStreamAdapter extends InputStream {
|
||||
private ImageInputStream mInput;
|
||||
private final boolean mHasLength;
|
||||
private long mLeft;
|
||||
private long mMarkPosition;
|
||||
private ImageInputStream input;
|
||||
private final boolean hasLength;
|
||||
private long left;
|
||||
private long markPosition;
|
||||
|
||||
// TODO: Enforce stream boundaries!
|
||||
// TODO: Stream start position....
|
||||
@@ -82,9 +82,9 @@ class IIOInputStreamAdapter extends InputStream {
|
||||
throw new IllegalArgumentException("length < 0");
|
||||
}
|
||||
|
||||
mInput = pInput;
|
||||
mHasLength = pHasLength;
|
||||
mLeft = pLength;
|
||||
input = pInput;
|
||||
hasLength = pHasLength;
|
||||
left = pLength;
|
||||
}
|
||||
|
||||
|
||||
@@ -93,17 +93,17 @@ class IIOInputStreamAdapter extends InputStream {
|
||||
* This implementation does <em>not</em> close the underlying stream.
|
||||
*/
|
||||
public void close() throws IOException {
|
||||
if (mHasLength) {
|
||||
mInput.seek(mInput.getStreamPosition() + mLeft);
|
||||
if (hasLength) {
|
||||
input.seek(input.getStreamPosition() + left);
|
||||
}
|
||||
|
||||
mLeft = 0;
|
||||
mInput = null;
|
||||
left = 0;
|
||||
input = null;
|
||||
}
|
||||
|
||||
public int available() throws IOException {
|
||||
if (mHasLength) {
|
||||
return mLeft > 0 ? (int) Math.min(Integer.MAX_VALUE, mLeft) : 0;
|
||||
if (hasLength) {
|
||||
return left > 0 ? (int) Math.min(Integer.MAX_VALUE, left) : 0;
|
||||
}
|
||||
return 0; // We don't really know, so we say 0 to be safe.
|
||||
}
|
||||
@@ -115,7 +115,7 @@ class IIOInputStreamAdapter extends InputStream {
|
||||
|
||||
public void mark(int pReadLimit) {
|
||||
try {
|
||||
mMarkPosition = mInput.getStreamPosition();
|
||||
markPosition = input.getStreamPosition();
|
||||
}
|
||||
catch (IOException e) {
|
||||
// Let's hope this never happens, because it's not possible to reset then...
|
||||
@@ -124,17 +124,17 @@ class IIOInputStreamAdapter extends InputStream {
|
||||
}
|
||||
|
||||
public void reset() throws IOException {
|
||||
long diff = mInput.getStreamPosition() - mMarkPosition;
|
||||
mInput.seek(mMarkPosition);
|
||||
mLeft += diff;
|
||||
long diff = input.getStreamPosition() - markPosition;
|
||||
input.seek(markPosition);
|
||||
left += diff;
|
||||
}
|
||||
|
||||
public int read() throws IOException {
|
||||
if (mHasLength && mLeft-- <= 0) {
|
||||
mLeft = 0;
|
||||
if (hasLength && left-- <= 0) {
|
||||
left = 0;
|
||||
return -1;
|
||||
}
|
||||
return mInput.read();
|
||||
return input.read();
|
||||
}
|
||||
|
||||
public final int read(byte[] pBytes) throws IOException {
|
||||
@@ -142,13 +142,13 @@ class IIOInputStreamAdapter extends InputStream {
|
||||
}
|
||||
|
||||
public int read(final byte[] pBytes, final int pOffset, final int pLength) throws IOException {
|
||||
if (mHasLength && mLeft <= 0) {
|
||||
if (hasLength && left <= 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
int read = mInput.read(pBytes, pOffset, (int) findMaxLen(pLength));
|
||||
if (mHasLength) {
|
||||
mLeft = read < 0 ? 0 : mLeft - read;
|
||||
int read = input.read(pBytes, pOffset, (int) findMaxLen(pLength));
|
||||
if (hasLength) {
|
||||
left = read < 0 ? 0 : left - read;
|
||||
}
|
||||
return read;
|
||||
}
|
||||
@@ -161,8 +161,8 @@ class IIOInputStreamAdapter extends InputStream {
|
||||
* @return the maximum number of bytes to read
|
||||
*/
|
||||
private long findMaxLen(long pLength) {
|
||||
if (mHasLength && mLeft < pLength) {
|
||||
return Math.max(mLeft, 0);
|
||||
if (hasLength && left < pLength) {
|
||||
return Math.max(left, 0);
|
||||
}
|
||||
else {
|
||||
return Math.max(pLength, 0);
|
||||
@@ -170,8 +170,8 @@ class IIOInputStreamAdapter extends InputStream {
|
||||
}
|
||||
|
||||
public long skip(long pLength) throws IOException {
|
||||
long skipped = mInput.skipBytes(findMaxLen(pLength)); // Skips 0 or more, never -1
|
||||
mLeft -= skipped;
|
||||
long skipped = input.skipBytes(findMaxLen(pLength)); // Skips 0 or more, never -1
|
||||
left -= skipped;
|
||||
return skipped;
|
||||
}
|
||||
}
|
||||
|
@@ -40,34 +40,34 @@ import java.io.OutputStream;
|
||||
* @version $Id: IIOOutputStreamAdapter.java,v 1.0 Sep 26, 2007 11:50:38 AM haraldk Exp$
|
||||
*/
|
||||
class IIOOutputStreamAdapter extends OutputStream {
|
||||
private ImageOutputStream mOutput;
|
||||
private ImageOutputStream output;
|
||||
|
||||
public IIOOutputStreamAdapter(final ImageOutputStream pOutput) {
|
||||
mOutput = pOutput;
|
||||
output = pOutput;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(final byte[] pBytes) throws IOException {
|
||||
mOutput.write(pBytes);
|
||||
output.write(pBytes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(final byte[] pBytes, final int pOffset, final int pLength) throws IOException {
|
||||
mOutput.write(pBytes, pOffset, pLength);
|
||||
output.write(pBytes, pOffset, pLength);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(final int pByte) throws IOException {
|
||||
mOutput.write(pByte);
|
||||
output.write(pByte);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flush() throws IOException {
|
||||
mOutput.flush();
|
||||
output.flush();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
mOutput = null;
|
||||
output = null;
|
||||
}
|
||||
}
|
||||
|
@@ -47,15 +47,15 @@ import java.util.Map;
|
||||
* @version $Id: ReaderFileSuffixFilter.java,v 1.0 11.okt.2006 20:05:36 haku Exp$
|
||||
*/
|
||||
public final class ReaderFileSuffixFilter extends FileFilter implements java.io.FileFilter {
|
||||
private final String mDescription;
|
||||
private final Map<String, Boolean> mKnownSuffixes = new HashMap<String, Boolean>(32);
|
||||
private final String description;
|
||||
private final Map<String, Boolean> knownSuffixes = new HashMap<String, Boolean>(32);
|
||||
|
||||
public ReaderFileSuffixFilter() {
|
||||
this("Images (all supported input formats)");
|
||||
}
|
||||
|
||||
public ReaderFileSuffixFilter(String pDescription) {
|
||||
mDescription = pDescription;
|
||||
description = pDescription;
|
||||
}
|
||||
|
||||
public boolean accept(File pFile) {
|
||||
@@ -71,19 +71,20 @@ public final class ReaderFileSuffixFilter extends FileFilter implements java.io.
|
||||
}
|
||||
|
||||
private boolean hasReaderForSuffix(String pSuffix) {
|
||||
if (mKnownSuffixes.get(pSuffix) == Boolean.TRUE) {
|
||||
if (knownSuffixes.get(pSuffix) == Boolean.TRUE) {
|
||||
return true;
|
||||
}
|
||||
|
||||
try {
|
||||
// Cahce lookup
|
||||
Iterator iterator = ImageIO.getImageReadersBySuffix(pSuffix);
|
||||
|
||||
if (iterator.hasNext()) {
|
||||
mKnownSuffixes.put(pSuffix, Boolean.TRUE);
|
||||
knownSuffixes.put(pSuffix, Boolean.TRUE);
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
mKnownSuffixes.put(pSuffix, Boolean.FALSE);
|
||||
knownSuffixes.put(pSuffix, Boolean.FALSE);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -93,6 +94,6 @@ public final class ReaderFileSuffixFilter extends FileFilter implements java.io.
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return mDescription;
|
||||
return description;
|
||||
}
|
||||
}
|
||||
|
@@ -47,15 +47,15 @@ import java.util.Map;
|
||||
* @version $Id: WriterFileSuffixFilter.java,v 1.0 11.okt.2006 20:05:36 haku Exp$
|
||||
*/
|
||||
public final class WriterFileSuffixFilter extends FileFilter implements java.io.FileFilter {
|
||||
private final String mDescription;
|
||||
private Map<String, Boolean>mKnownSuffixes = new HashMap<String, Boolean>(32);
|
||||
private final String description;
|
||||
private Map<String, Boolean> knownSuffixes = new HashMap<String, Boolean>(32);
|
||||
|
||||
public WriterFileSuffixFilter() {
|
||||
this("Images (all supported output formats)");
|
||||
}
|
||||
|
||||
public WriterFileSuffixFilter(String pDescription) {
|
||||
mDescription = pDescription;
|
||||
description = pDescription;
|
||||
}
|
||||
|
||||
public boolean accept(File pFile) {
|
||||
@@ -66,24 +66,25 @@ public final class WriterFileSuffixFilter extends FileFilter implements java.io.
|
||||
|
||||
// Test if we have an ImageWriter for this suffix
|
||||
String suffix = FileUtil.getExtension(pFile);
|
||||
return !StringUtil.isEmpty(suffix) && hasWriterForSuffix(suffix);
|
||||
|
||||
return !StringUtil.isEmpty(suffix) && hasWriterForSuffix(suffix);
|
||||
}
|
||||
|
||||
private boolean hasWriterForSuffix(String pSuffix) {
|
||||
if (mKnownSuffixes.get(pSuffix) == Boolean.TRUE) {
|
||||
if (knownSuffixes.get(pSuffix) == Boolean.TRUE) {
|
||||
return true;
|
||||
}
|
||||
|
||||
try {
|
||||
// Cahce lookup
|
||||
Iterator iterator = ImageIO.getImageWritersBySuffix(pSuffix);
|
||||
|
||||
if (iterator.hasNext()) {
|
||||
mKnownSuffixes.put(pSuffix, Boolean.TRUE);
|
||||
knownSuffixes.put(pSuffix, Boolean.TRUE);
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
mKnownSuffixes.put(pSuffix, Boolean.FALSE);
|
||||
knownSuffixes.put(pSuffix, Boolean.FALSE);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -93,6 +94,6 @@ public final class WriterFileSuffixFilter extends FileFilter implements java.io.
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return mDescription;
|
||||
return description;
|
||||
}
|
||||
}
|
||||
|
@@ -18,7 +18,7 @@ import java.util.Random;
|
||||
* @version $Id: BufferedImageInputStreamTestCase.java,v 1.0 Jun 30, 2008 3:07:42 PM haraldk Exp$
|
||||
*/
|
||||
public class BufferedImageInputStreamTestCase extends TestCase {
|
||||
protected final Random mRandom = new Random();
|
||||
protected final Random random = new Random();
|
||||
|
||||
public void testCreate() throws IOException {
|
||||
new BufferedImageInputStream(new ByteArrayImageInputStream(new byte[0]));
|
||||
@@ -58,7 +58,7 @@ public class BufferedImageInputStreamTestCase extends TestCase {
|
||||
|
||||
// Fill bytes
|
||||
byte[] bytes = new byte[size * 2];
|
||||
mRandom.nextBytes(bytes);
|
||||
random.nextBytes(bytes);
|
||||
|
||||
// Create wrapper stream
|
||||
BufferedImageInputStream stream = new BufferedImageInputStream(new ByteArrayImageInputStream(bytes));
|
||||
@@ -79,7 +79,7 @@ public class BufferedImageInputStreamTestCase extends TestCase {
|
||||
public void testBufferPositionCorrect() throws IOException {
|
||||
// Fill bytes
|
||||
byte[] bytes = new byte[1024];
|
||||
mRandom.nextBytes(bytes);
|
||||
random.nextBytes(bytes);
|
||||
|
||||
ByteArrayImageInputStream input = new ByteArrayImageInputStream(bytes);
|
||||
|
||||
|
@@ -1,11 +1,12 @@
|
||||
package com.twelvemonkeys.imageio.stream;
|
||||
|
||||
import static com.twelvemonkeys.imageio.stream.BufferedImageInputStreamTestCase.rangeEquals;
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Random;
|
||||
|
||||
import static com.twelvemonkeys.imageio.stream.BufferedImageInputStreamTestCase.rangeEquals;
|
||||
|
||||
/**
|
||||
* ByteArrayImageInputStreamTestCase
|
||||
*
|
||||
@@ -14,7 +15,7 @@ import java.util.Random;
|
||||
* @version $Id: ByteArrayImageInputStreamTestCase.java,v 1.0 Apr 21, 2009 10:58:48 AM haraldk Exp$
|
||||
*/
|
||||
public class ByteArrayImageInputStreamTestCase extends TestCase {
|
||||
protected final Random mRandom = new Random();
|
||||
protected final Random random = new Random();
|
||||
|
||||
public void testCreate() {
|
||||
ByteArrayImageInputStream stream = new ByteArrayImageInputStream(new byte[0]);
|
||||
@@ -36,7 +37,7 @@ public class ByteArrayImageInputStreamTestCase extends TestCase {
|
||||
|
||||
public void testRead() throws IOException {
|
||||
byte[] data = new byte[1024 * 1024];
|
||||
mRandom.nextBytes(data);
|
||||
random.nextBytes(data);
|
||||
|
||||
ByteArrayImageInputStream stream = new ByteArrayImageInputStream(data);
|
||||
|
||||
@@ -49,7 +50,7 @@ public class ByteArrayImageInputStreamTestCase extends TestCase {
|
||||
|
||||
public void testReadArray() throws IOException {
|
||||
byte[] data = new byte[1024 * 1024];
|
||||
mRandom.nextBytes(data);
|
||||
random.nextBytes(data);
|
||||
|
||||
ByteArrayImageInputStream stream = new ByteArrayImageInputStream(data);
|
||||
|
||||
@@ -65,7 +66,7 @@ public class ByteArrayImageInputStreamTestCase extends TestCase {
|
||||
|
||||
public void testReadSkip() throws IOException {
|
||||
byte[] data = new byte[1024 * 14];
|
||||
mRandom.nextBytes(data);
|
||||
random.nextBytes(data);
|
||||
|
||||
ByteArrayImageInputStream stream = new ByteArrayImageInputStream(data);
|
||||
|
||||
@@ -82,7 +83,7 @@ public class ByteArrayImageInputStreamTestCase extends TestCase {
|
||||
|
||||
public void testReadSeek() throws IOException {
|
||||
byte[] data = new byte[1024 * 18];
|
||||
mRandom.nextBytes(data);
|
||||
random.nextBytes(data);
|
||||
|
||||
ByteArrayImageInputStream stream = new ByteArrayImageInputStream(data);
|
||||
|
||||
|
@@ -19,12 +19,12 @@ import java.util.Random;
|
||||
*/
|
||||
public class SubImageInputStreamTestCase extends TestCase {
|
||||
// TODO: Extract super test case for all stream tests
|
||||
private final Random mRandom = new Random(837468l);
|
||||
private final Random random = new Random(837468l);
|
||||
|
||||
private ImageInputStream createStream(final int pSize) {
|
||||
byte[] bytes = new byte[pSize];
|
||||
|
||||
mRandom.nextBytes(bytes);
|
||||
random.nextBytes(bytes);
|
||||
|
||||
return new MemoryCacheImageInputStream(new ByteArrayInputStream(bytes)) {
|
||||
@Override
|
||||
|
@@ -116,6 +116,10 @@ public abstract class ImageReaderAbstractTestCase<T extends ImageReader> extends
|
||||
|
||||
protected abstract List<String> getMIMETypes();
|
||||
|
||||
protected boolean allowsNullRawImageType() {
|
||||
return false;
|
||||
}
|
||||
|
||||
protected void assertProviderInstalledForName(final String pFormat, final Class<? extends ImageReader> pReaderClass) {
|
||||
assertProviderInstalled0(pFormat.toUpperCase(), pReaderClass, ImageIO.getImageReadersByFormatName(pFormat.toUpperCase()));
|
||||
assertProviderInstalled0(pFormat.toLowerCase(), pReaderClass, ImageIO.getImageReadersByFormatName(pFormat.toLowerCase()));
|
||||
@@ -1111,30 +1115,34 @@ public abstract class ImageReaderAbstractTestCase<T extends ImageReader> extends
|
||||
|
||||
public void testGetTypeSpecifiers() throws IOException {
|
||||
final ImageReader reader = createReader();
|
||||
TestData data = getTestData().get(0);
|
||||
reader.setInput(data.getInputStream());
|
||||
for (TestData data : getTestData()) {
|
||||
reader.setInput(data.getInputStream());
|
||||
|
||||
ImageTypeSpecifier rawType = reader.getRawImageType(0);
|
||||
assertNotNull(rawType);
|
||||
|
||||
Iterator<ImageTypeSpecifier> types = reader.getImageTypes(0);
|
||||
|
||||
assertNotNull(types);
|
||||
assertTrue(types.hasNext());
|
||||
|
||||
// TODO: This might fail even though the specifiers are obviously equal, if the
|
||||
// color spaces they use are not the SAME instance, as ColorSpace uses identity equals
|
||||
// and Interleaved ImageTypeSpecifiers are only equal if color spaces are equal...
|
||||
boolean rawFound = false;
|
||||
while (types.hasNext()) {
|
||||
ImageTypeSpecifier type = types.next();
|
||||
if (type.equals(rawType)) {
|
||||
rawFound = true;
|
||||
break;
|
||||
ImageTypeSpecifier rawType = reader.getRawImageType(0);
|
||||
if (rawType == null && allowsNullRawImageType()) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
assertNotNull(rawType);
|
||||
|
||||
assertTrue("ImageTypeSepcifier from getRawImageType should be in the iterator from getImageTypes", rawFound);
|
||||
Iterator<ImageTypeSpecifier> types = reader.getImageTypes(0);
|
||||
|
||||
assertNotNull(types);
|
||||
assertTrue(types.hasNext());
|
||||
|
||||
// TODO: This might fail even though the specifiers are obviously equal, if the
|
||||
// color spaces they use are not the SAME instance, as ColorSpace uses identity equals
|
||||
// and Interleaved ImageTypeSpecifiers are only equal if color spaces are equal...
|
||||
boolean rawFound = false;
|
||||
while (types.hasNext()) {
|
||||
ImageTypeSpecifier type = types.next();
|
||||
if (type.equals(rawType)) {
|
||||
rawFound = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
assertTrue("ImageTypeSepcifier from getRawImageType should be in the iterator from getImageTypes", rawFound);
|
||||
}
|
||||
}
|
||||
|
||||
public void testSetDestination() throws IOException {
|
||||
@@ -1164,12 +1172,18 @@ public abstract class ImageReaderAbstractTestCase<T extends ImageReader> extends
|
||||
ImageReadParam param = reader.getDefaultReadParam();
|
||||
|
||||
ImageTypeSpecifier type = reader.getRawImageType(0);
|
||||
BufferedImage destination = type.createBufferedImage(reader.getWidth(0), reader.getHeight(0));
|
||||
param.setDestination(destination);
|
||||
|
||||
BufferedImage result = reader.read(0, param);
|
||||
if (type != null) {
|
||||
BufferedImage destination = type.createBufferedImage(reader.getWidth(0), reader.getHeight(0));
|
||||
param.setDestination(destination);
|
||||
|
||||
assertSame(destination, result);
|
||||
BufferedImage result = reader.read(0, param);
|
||||
|
||||
assertSame(destination, result);
|
||||
}
|
||||
else {
|
||||
System.err.println("WARNING: Test skipped due to reader.getRawImageType(0) returning null");
|
||||
}
|
||||
}
|
||||
|
||||
public void testSetDestinationIllegal() throws IOException {
|
||||
@@ -1258,7 +1272,7 @@ public abstract class ImageReaderAbstractTestCase<T extends ImageReader> extends
|
||||
boolean removed = illegalTypes.remove(valid);
|
||||
|
||||
// TODO: 4BYTE_ABGR (6) and 4BYTE_ABGR_PRE (7) is essentially the same type...
|
||||
// !#$#<23>%$! ImageTypeSpecifier.equals is not well-defined
|
||||
// !#$#<23>%$! ImageTypeSpecifier.equals is not well-defined
|
||||
if (!removed) {
|
||||
for (Iterator<ImageTypeSpecifier> iterator = illegalTypes.iterator(); iterator.hasNext();) {
|
||||
ImageTypeSpecifier illegalType = iterator.next();
|
||||
|
@@ -1,7 +1,7 @@
|
||||
- Rename to imageio-common?
|
||||
- Separate modules for more for more plugins
|
||||
- The BMP reader spports some special formats not supported by Sun reader
|
||||
- PNM package is pretty complete, but useless, as it's provided by Sun? Licencse?
|
||||
- The BMP reader supports some special formats not supported by Sun reader
|
||||
- PNM package is pretty complete, but useless, as it's provided by Sun? License?
|
||||
- WBMP?
|
||||
- XBM?
|
||||
DONE:
|
||||
|
@@ -78,7 +78,7 @@ class BitmapIndexed extends BitmapDescriptor {
|
||||
|
||||
WritableRaster raster = image.getRaster();
|
||||
|
||||
// Make pixels transparant according to mask
|
||||
// Make pixels transparent according to mask
|
||||
final int trans = icm.getTransparentPixel();
|
||||
for (int y = 0; y < getHeight(); y++) {
|
||||
for (int x = 0; x < getWidth(); x++) {
|
||||
|
@@ -132,8 +132,8 @@ public class ICOImageReader extends ImageReaderBase {
|
||||
case 8:
|
||||
// TODO: This is slightly QnD...
|
||||
int offset = entry.getOffset() + header.getSize();
|
||||
if (offset != mImageInput.getStreamPosition()) {
|
||||
mImageInput.seek(offset);
|
||||
if (offset != imageInput.getStreamPosition()) {
|
||||
imageInput.seek(offset);
|
||||
}
|
||||
BitmapIndexed indexed = new BitmapIndexed(entry, header);
|
||||
readColorMap(indexed);
|
||||
@@ -220,21 +220,17 @@ public class ICOImageReader extends ImageReaderBase {
|
||||
return destination;
|
||||
}
|
||||
|
||||
private boolean hasExplicitDestination(final ImageReadParam pParam) {
|
||||
return (pParam != null && (pParam.getDestination() != null || pParam.getDestinationType() != null || pParam.getDestinationOffset() != null));
|
||||
}
|
||||
|
||||
private boolean isPNG(final DirectoryEntry pEntry) throws IOException {
|
||||
long magic;
|
||||
|
||||
mImageInput.seek(pEntry.getOffset());
|
||||
mImageInput.setByteOrder(ByteOrder.BIG_ENDIAN);
|
||||
imageInput.seek(pEntry.getOffset());
|
||||
imageInput.setByteOrder(ByteOrder.BIG_ENDIAN);
|
||||
|
||||
try {
|
||||
magic = mImageInput.readLong();
|
||||
magic = imageInput.readLong();
|
||||
}
|
||||
finally {
|
||||
mImageInput.setByteOrder(ByteOrder.LITTLE_ENDIAN);
|
||||
imageInput.setByteOrder(ByteOrder.LITTLE_ENDIAN);
|
||||
}
|
||||
|
||||
return magic == DIB.PNG_MAGIC;
|
||||
@@ -252,8 +248,8 @@ public class ICOImageReader extends ImageReaderBase {
|
||||
private ImageReader initPNGReader(final DirectoryEntry pEntry) throws IOException {
|
||||
ImageReader pngReader = getPNGReader();
|
||||
|
||||
mImageInput.seek(pEntry.getOffset());
|
||||
InputStream inputStream = IIOUtil.createStreamAdapter(mImageInput, pEntry.getSize());
|
||||
imageInput.seek(pEntry.getOffset());
|
||||
InputStream inputStream = IIOUtil.createStreamAdapter(imageInput, pEntry.getSize());
|
||||
ImageInputStream stream = ImageIO.createImageInputStream(inputStream);
|
||||
|
||||
// NOTE: Will throw IOException on later reads if input is not PNG
|
||||
@@ -283,8 +279,8 @@ public class ICOImageReader extends ImageReaderBase {
|
||||
|
||||
private DIBHeader getHeader(final DirectoryEntry pEntry) throws IOException {
|
||||
if (!mHeaders.containsKey(pEntry)) {
|
||||
mImageInput.seek(pEntry.getOffset());
|
||||
DIBHeader header = DIBHeader.read(mImageInput);
|
||||
imageInput.seek(pEntry.getOffset());
|
||||
DIBHeader header = DIBHeader.read(imageInput);
|
||||
mHeaders.put(pEntry, header);
|
||||
}
|
||||
|
||||
@@ -299,8 +295,8 @@ public class ICOImageReader extends ImageReaderBase {
|
||||
DIBHeader header = getHeader(pEntry);
|
||||
|
||||
int offset = pEntry.getOffset() + header.getSize();
|
||||
if (offset != mImageInput.getStreamPosition()) {
|
||||
mImageInput.seek(offset);
|
||||
if (offset != imageInput.getStreamPosition()) {
|
||||
imageInput.seek(offset);
|
||||
}
|
||||
|
||||
// TODO: Support this, it's already in the BMP reader, spec allows RLE4 and RLE8
|
||||
@@ -368,7 +364,7 @@ public class ICOImageReader extends ImageReaderBase {
|
||||
|
||||
for (int i = 0; i < colorCount; i++) {
|
||||
// aRGB (a is "Reserved")
|
||||
pBitmap.mColors[i] = (mImageInput.readInt() & 0xffffff) | 0xff000000;
|
||||
pBitmap.mColors[i] = (imageInput.readInt() & 0xffffff) | 0xff000000;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -377,7 +373,7 @@ public class ICOImageReader extends ImageReaderBase {
|
||||
byte[] row = new byte[width];
|
||||
|
||||
for (int y = 0; y < pBitmap.getHeight(); y++) {
|
||||
mImageInput.readFully(row, 0, width);
|
||||
imageInput.readFully(row, 0, width);
|
||||
int rowPos = 0;
|
||||
int xOrVal = 0x80;
|
||||
int pos = (pBitmap.getHeight() - y - 1) * pBitmap.getWidth();
|
||||
@@ -411,7 +407,7 @@ public class ICOImageReader extends ImageReaderBase {
|
||||
byte[] row = new byte[width];
|
||||
|
||||
for (int y = 0; y < pBitmap.getHeight(); y++) {
|
||||
mImageInput.readFully(row, 0, width);
|
||||
imageInput.readFully(row, 0, width);
|
||||
int rowPos = 0;
|
||||
boolean high4 = true;
|
||||
int pos = (pBitmap.getHeight() - y - 1) * pBitmap.getWidth();
|
||||
@@ -446,7 +442,7 @@ public class ICOImageReader extends ImageReaderBase {
|
||||
byte[] row = new byte[width];
|
||||
|
||||
for (int y = 0; y < pBitmap.getHeight(); y++) {
|
||||
mImageInput.readFully(row, 0, width);
|
||||
imageInput.readFully(row, 0, width);
|
||||
int rowPos = 0;
|
||||
int pos = (pBitmap.getHeight() - y - 1) * pBitmap.getWidth();
|
||||
|
||||
@@ -488,12 +484,12 @@ public class ICOImageReader extends ImageReaderBase {
|
||||
|
||||
for (int y = 0; y < pBitmap.getHeight(); y++) {
|
||||
int offset = (pBitmap.getHeight() - y - 1) * pBitmap.getWidth();
|
||||
mImageInput.readFully(pixels, offset, pBitmap.getWidth());
|
||||
imageInput.readFully(pixels, offset, pBitmap.getWidth());
|
||||
|
||||
|
||||
// Skip to 32 bit boundary
|
||||
if (pBitmap.getWidth() % 2 != 0) {
|
||||
mImageInput.readShort();
|
||||
imageInput.readShort();
|
||||
}
|
||||
|
||||
if (abortRequested()) {
|
||||
@@ -524,7 +520,7 @@ public class ICOImageReader extends ImageReaderBase {
|
||||
|
||||
for (int y = 0; y < pBitmap.getHeight(); y++) {
|
||||
int offset = (pBitmap.getHeight() - y - 1) * pBitmap.getWidth();
|
||||
mImageInput.readFully(pixels, offset, pBitmap.getWidth() * 3);
|
||||
imageInput.readFully(pixels, offset, pBitmap.getWidth() * 3);
|
||||
|
||||
// TODO: Possibly read padding byte here!
|
||||
|
||||
@@ -550,7 +546,7 @@ public class ICOImageReader extends ImageReaderBase {
|
||||
|
||||
for (int y = 0; y < pBitmap.getHeight(); y++) {
|
||||
int offset = (pBitmap.getHeight() - y - 1) * pBitmap.getWidth();
|
||||
mImageInput.readFully(pixels, offset, pBitmap.getWidth());
|
||||
imageInput.readFully(pixels, offset, pBitmap.getWidth());
|
||||
|
||||
if (abortRequested()) {
|
||||
processReadAborted();
|
||||
@@ -571,17 +567,17 @@ public class ICOImageReader extends ImageReaderBase {
|
||||
}
|
||||
|
||||
private void readFileHeader() throws IOException {
|
||||
mImageInput.setByteOrder(ByteOrder.LITTLE_ENDIAN);
|
||||
imageInput.setByteOrder(ByteOrder.LITTLE_ENDIAN);
|
||||
|
||||
// Read file header
|
||||
mImageInput.readUnsignedShort(); // Reserved
|
||||
imageInput.readUnsignedShort(); // Reserved
|
||||
|
||||
// Should be same as type as the provider
|
||||
int type = mImageInput.readUnsignedShort();
|
||||
int imageCount = mImageInput.readUnsignedShort();
|
||||
int type = imageInput.readUnsignedShort();
|
||||
int imageCount = imageInput.readUnsignedShort();
|
||||
|
||||
// Read directory
|
||||
mDirectory = Directory.read(type, imageCount, mImageInput);
|
||||
mDirectory = Directory.read(type, imageCount, imageInput);
|
||||
}
|
||||
|
||||
final DirectoryEntry getEntry(final int pImageIndex) throws IOException {
|
||||
|
@@ -135,13 +135,13 @@ public class IFFImageReader extends ImageReaderBase {
|
||||
}
|
||||
|
||||
private void readMeta() throws IOException {
|
||||
if (mImageInput.readInt() != IFF.CHUNK_FORM) {
|
||||
if (imageInput.readInt() != IFF.CHUNK_FORM) {
|
||||
throw new IIOException("Unknown file format for IFFImageReader");
|
||||
}
|
||||
|
||||
int remaining = mImageInput.readInt() - 4; // We'll read 4 more in a sec
|
||||
int remaining = imageInput.readInt() - 4; // We'll read 4 more in a sec
|
||||
|
||||
mFormType = mImageInput.readInt();
|
||||
mFormType = imageInput.readInt();
|
||||
if (mFormType != IFF.TYPE_ILBM && mFormType != IFF.TYPE_PBM) {
|
||||
throw new IIOException("Only IFF (FORM) type ILBM and PBM supported: " + IFFUtil.toChunkStr(mFormType));
|
||||
}
|
||||
@@ -152,8 +152,8 @@ public class IFFImageReader extends ImageReaderBase {
|
||||
mViewPort = null;
|
||||
|
||||
while (remaining > 0) {
|
||||
int chunkId = mImageInput.readInt();
|
||||
int length = mImageInput.readInt();
|
||||
int chunkId = imageInput.readInt();
|
||||
int length = imageInput.readInt();
|
||||
|
||||
remaining -= 8;
|
||||
remaining -= length % 2 == 0 ? length : length + 1;
|
||||
@@ -168,7 +168,7 @@ public class IFFImageReader extends ImageReaderBase {
|
||||
}
|
||||
|
||||
mHeader = new BMHDChunk(length);
|
||||
mHeader.readChunk(mImageInput);
|
||||
mHeader.readChunk(imageInput);
|
||||
|
||||
//System.out.println(mHeader);
|
||||
break;
|
||||
@@ -177,7 +177,7 @@ public class IFFImageReader extends ImageReaderBase {
|
||||
throw new IIOException("Multiple CMAP chunks not allowed");
|
||||
}
|
||||
mColorMap = new CMAPChunk(length, mHeader, mViewPort);
|
||||
mColorMap.readChunk(mImageInput);
|
||||
mColorMap.readChunk(imageInput);
|
||||
|
||||
//System.out.println(mColorMap);
|
||||
break;
|
||||
@@ -186,7 +186,7 @@ public class IFFImageReader extends ImageReaderBase {
|
||||
throw new IIOException("Multiple GRAB chunks not allowed");
|
||||
}
|
||||
mGrab = new GRABChunk(length);
|
||||
mGrab.readChunk(mImageInput);
|
||||
mGrab.readChunk(imageInput);
|
||||
|
||||
//System.out.println(mGrab);
|
||||
break;
|
||||
@@ -195,7 +195,7 @@ public class IFFImageReader extends ImageReaderBase {
|
||||
throw new IIOException("Multiple CAMG chunks not allowed");
|
||||
}
|
||||
mViewPort = new CAMGChunk(length);
|
||||
mViewPort.readChunk(mImageInput);
|
||||
mViewPort.readChunk(imageInput);
|
||||
|
||||
//System.out.println(mViewPort);
|
||||
break;
|
||||
@@ -205,7 +205,7 @@ public class IFFImageReader extends ImageReaderBase {
|
||||
}
|
||||
|
||||
mBody = new BODYChunk(length);
|
||||
mBodyStart = mImageInput.getStreamPosition();
|
||||
mBodyStart = imageInput.getStreamPosition();
|
||||
|
||||
// NOTE: We don't read the body here, it's done later in the read(int, ImageReadParam) method
|
||||
|
||||
@@ -215,7 +215,7 @@ public class IFFImageReader extends ImageReaderBase {
|
||||
// TODO: We probably want to store anno chunks as Metadata
|
||||
// ANNO, DEST, SPRT and more
|
||||
IFFChunk generic = new GenericChunk(chunkId, length);
|
||||
generic.readChunk(mImageInput);
|
||||
generic.readChunk(imageInput);
|
||||
|
||||
//System.out.println(generic);
|
||||
break;
|
||||
@@ -323,16 +323,16 @@ public class IFFImageReader extends ImageReaderBase {
|
||||
}
|
||||
|
||||
private void readBody(final ImageReadParam pParam) throws IOException {
|
||||
mImageInput.seek(mBodyStart);
|
||||
imageInput.seek(mBodyStart);
|
||||
mByteRunStream = null;
|
||||
|
||||
// NOTE: mColorMap may be null for 8 bit (gray), 24 bit or 32 bit only
|
||||
if (mColorMap != null) {
|
||||
IndexColorModel cm = mColorMap.getIndexColorModel();
|
||||
readIndexed(pParam, mImageInput, cm);
|
||||
readIndexed(pParam, imageInput, cm);
|
||||
}
|
||||
else {
|
||||
readTrueColor(pParam, mImageInput);
|
||||
readTrueColor(pParam, imageInput);
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -99,22 +99,22 @@ public class IFFImageWriter extends ImageWriterBase {
|
||||
}
|
||||
|
||||
private void writeBody(ByteArrayOutputStream pImageData) throws IOException {
|
||||
mImageOutput.writeInt(IFF.CHUNK_BODY);
|
||||
mImageOutput.writeInt(pImageData.size());
|
||||
imageOutput.writeInt(IFF.CHUNK_BODY);
|
||||
imageOutput.writeInt(pImageData.size());
|
||||
|
||||
// NOTE: This is much faster than mOutput.write(pImageData.toByteArray())
|
||||
// as the data array is not duplicated
|
||||
pImageData.writeTo(IIOUtil.createStreamAdapter(mImageOutput));
|
||||
pImageData.writeTo(IIOUtil.createStreamAdapter(imageOutput));
|
||||
|
||||
if (pImageData.size() % 2 == 0) {
|
||||
mImageOutput.writeByte(0); // PAD
|
||||
imageOutput.writeByte(0); // PAD
|
||||
}
|
||||
|
||||
// NOTE: Most progress is done in packImageData, however, as we need to
|
||||
// buffer, to write correct size, we defer the last 10 percent until now.
|
||||
processImageProgress(100f);
|
||||
|
||||
mImageOutput.flush();
|
||||
imageOutput.flush();
|
||||
}
|
||||
|
||||
private void packImageData(OutputStream pOutput, RenderedImage pImage, ImageWriteParam pParam) throws IOException {
|
||||
@@ -213,16 +213,16 @@ public class IFFImageWriter extends ImageWriterBase {
|
||||
size += 8 + cmap.mChunkLength;
|
||||
}
|
||||
|
||||
mImageOutput.writeInt(IFF.CHUNK_FORM);
|
||||
mImageOutput.writeInt(size);
|
||||
imageOutput.writeInt(IFF.CHUNK_FORM);
|
||||
imageOutput.writeInt(size);
|
||||
|
||||
mImageOutput.writeInt(IFF.TYPE_ILBM);
|
||||
imageOutput.writeInt(IFF.TYPE_ILBM);
|
||||
|
||||
anno.writeChunk(mImageOutput);
|
||||
header.writeChunk(mImageOutput);
|
||||
anno.writeChunk(imageOutput);
|
||||
header.writeChunk(imageOutput);
|
||||
if (cmap != null) {
|
||||
//System.out.println("CMAP written");
|
||||
cmap.writeChunk(mImageOutput);
|
||||
cmap.writeChunk(imageOutput);
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -286,11 +286,11 @@ abstract class JMagickReader extends ImageReaderBase {
|
||||
// TODO: If ImageInputStream is already file-backed, maybe we can peek into that file?
|
||||
// At the moment, the cache/file is not accessible, but we could create our own
|
||||
// FileImageInputStream provider that gives us this access.
|
||||
if (!mUseTempFile && mImageInput.length() >= 0 && mImageInput.length() <= Integer.MAX_VALUE) {
|
||||
if (!mUseTempFile && imageInput.length() >= 0 && imageInput.length() <= Integer.MAX_VALUE) {
|
||||
// This works for most file formats, as long as ImageMagick
|
||||
// uses the file magic to decide file format
|
||||
byte[] bytes = new byte[(int) mImageInput.length()];
|
||||
mImageInput.readFully(bytes);
|
||||
byte[] bytes = new byte[(int) imageInput.length()];
|
||||
imageInput.readFully(bytes);
|
||||
|
||||
// Unfortunately, this is a waste of space & time...
|
||||
ImageInfo info = new ImageInfo();
|
||||
@@ -309,7 +309,7 @@ abstract class JMagickReader extends ImageReaderBase {
|
||||
byte[] buffer = new byte[FileUtil.BUF_SIZE];
|
||||
int count;
|
||||
|
||||
while ((count = mImageInput.read(buffer)) != -1) {
|
||||
while ((count = imageInput.read(buffer)) != -1) {
|
||||
out.write(buffer, 0, count);
|
||||
}
|
||||
|
||||
|
@@ -113,8 +113,8 @@ abstract class JMagickWriter extends ImageWriterBase {
|
||||
processImageProgress(67);
|
||||
|
||||
// Write blob to output
|
||||
mImageOutput.write(bytes);
|
||||
mImageOutput.flush();
|
||||
imageOutput.write(bytes);
|
||||
imageOutput.flush();
|
||||
processImageProgress(100);
|
||||
}
|
||||
catch (MagickException e) {
|
||||
|
@@ -506,12 +506,12 @@ public class JPEGImageReader extends ImageReaderBase {
|
||||
}
|
||||
|
||||
private void readSegments() throws IOException {
|
||||
long pos = mImageInput.getStreamPosition();
|
||||
long pos = imageInput.getStreamPosition();
|
||||
|
||||
try {
|
||||
mImageInput.seek(0); // TODO: Seek to wanted image
|
||||
imageInput.seek(0); // TODO: Seek to wanted image
|
||||
|
||||
segments = JPEGSegmentUtil.readSegments(mImageInput, SEGMENT_IDENTIFIERS);
|
||||
segments = JPEGSegmentUtil.readSegments(imageInput, SEGMENT_IDENTIFIERS);
|
||||
}
|
||||
catch (IOException ignore) {
|
||||
}
|
||||
@@ -519,7 +519,7 @@ public class JPEGImageReader extends ImageReaderBase {
|
||||
foo.printStackTrace();
|
||||
}
|
||||
finally {
|
||||
mImageInput.seek(pos);
|
||||
imageInput.seek(pos);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -31,6 +31,7 @@ package com.twelvemonkeys.imageio.metadata;
|
||||
import com.twelvemonkeys.lang.Validate;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* AbstractEntry
|
||||
@@ -69,6 +70,45 @@ public abstract class AbstractEntry implements Entry {
|
||||
}
|
||||
|
||||
public String getValueAsString() {
|
||||
if (valueCount() > 1) {
|
||||
if (valueCount() < 16) {
|
||||
Class<?> type = mValue.getClass().getComponentType();
|
||||
|
||||
if (type.isPrimitive()) {
|
||||
if (type.equals(boolean.class)) {
|
||||
return Arrays.toString((boolean[]) mValue);
|
||||
}
|
||||
else if (type.equals(byte.class)) {
|
||||
return Arrays.toString((byte[]) mValue);
|
||||
}
|
||||
else if (type.equals(char.class)) {
|
||||
return new String((char[]) mValue);
|
||||
}
|
||||
else if (type.equals(double.class)) {
|
||||
return Arrays.toString((double[]) mValue);
|
||||
}
|
||||
else if (type.equals(float.class)) {
|
||||
return Arrays.toString((float[]) mValue);
|
||||
}
|
||||
else if (type.equals(int.class)) {
|
||||
return Arrays.toString((int[]) mValue);
|
||||
}
|
||||
else if (type.equals(long.class)) {
|
||||
return Arrays.toString((long[]) mValue);
|
||||
}
|
||||
else if (type.equals(short.class)) {
|
||||
return Arrays.toString((short[]) mValue);
|
||||
}
|
||||
// Fall through should never happen
|
||||
}
|
||||
else {
|
||||
return Arrays.toString((Object[]) mValue);
|
||||
}
|
||||
}
|
||||
|
||||
return String.valueOf(mValue) + " (" + valueCount() + ")";
|
||||
}
|
||||
|
||||
return String.valueOf(mValue);
|
||||
}
|
||||
|
||||
|
@@ -43,16 +43,35 @@ final class EXIFEntry extends AbstractEntry {
|
||||
EXIFEntry(final int pIdentifier, final Object pValue, final short pType) {
|
||||
super(pIdentifier, pValue);
|
||||
|
||||
if (pType < 1 || pType > TIFF.TYPE_NAMES.length) {
|
||||
throw new IllegalArgumentException(String.format("Illegal EXIF type: %s", pType));
|
||||
}
|
||||
// if (pType < 1 || pType > TIFF.TYPE_NAMES.length) {
|
||||
// throw new IllegalArgumentException(String.format("Illegal EXIF type: %s", pType));
|
||||
// }
|
||||
|
||||
mType = pType;
|
||||
}
|
||||
|
||||
public short getType() {
|
||||
return mType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFieldName() {
|
||||
switch ((Integer) getIdentifier()) {
|
||||
case TIFF.TAG_EXIF_IFD:
|
||||
return "EXIF";
|
||||
case TIFF.TAG_XMP:
|
||||
return "XMP";
|
||||
case TIFF.TAG_IPTC:
|
||||
return "IPTC";
|
||||
case TIFF.TAG_PHOTOSHOP:
|
||||
return "Adobe";
|
||||
case TIFF.TAG_ICC_PROFILE:
|
||||
return "ICC Profile";
|
||||
|
||||
case TIFF.TAG_IMAGE_WIDTH:
|
||||
return "ImageWidth";
|
||||
case TIFF.TAG_IMAGE_HEIGHT:
|
||||
return "ImageHeight";
|
||||
case TIFF.TAG_COMPRESSION:
|
||||
return "Compression";
|
||||
case TIFF.TAG_ORIENTATION:
|
||||
@@ -82,6 +101,7 @@ final class EXIFEntry extends AbstractEntry {
|
||||
return "PixelXDimension";
|
||||
case EXIF.TAG_PIXEL_Y_DIMENSION:
|
||||
return "PixelYDimension";
|
||||
|
||||
// TODO: More field names
|
||||
}
|
||||
|
||||
|
@@ -34,10 +34,15 @@ import com.twelvemonkeys.imageio.metadata.MetadataReader;
|
||||
import com.twelvemonkeys.lang.StringUtil;
|
||||
|
||||
import javax.imageio.IIOException;
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -48,6 +53,7 @@ import java.util.List;
|
||||
* @version $Id: EXIFReader.java,v 1.0 Nov 13, 2009 5:42:51 PM haraldk Exp$
|
||||
*/
|
||||
public final class EXIFReader extends MetadataReader {
|
||||
static final Collection<Integer> KNOWN_IFDS = Arrays.asList(TIFF.TAG_EXIF_IFD, TIFF.TAG_GPS_IFD, TIFF.TAG_INTEROP_IFD);
|
||||
|
||||
@Override
|
||||
public Directory read(final ImageInputStream pInput) throws IOException {
|
||||
@@ -56,10 +62,15 @@ public final class EXIFReader extends MetadataReader {
|
||||
if (bom[0] == 'I' && bom[1] == 'I') {
|
||||
pInput.setByteOrder(ByteOrder.LITTLE_ENDIAN);
|
||||
}
|
||||
else if (!(bom[0] == 'M' && bom[1] == 'M')) {
|
||||
else if (bom[0] == 'M' && bom[1] == 'M') {
|
||||
pInput.setByteOrder(ByteOrder.BIG_ENDIAN);
|
||||
}
|
||||
else {
|
||||
throw new IIOException(String.format("Invalid TIFF byte order mark '%s', expected: 'II' or 'MM'", StringUtil.decode(bom, 0, bom.length, "ASCII")));
|
||||
}
|
||||
|
||||
// TODO: BigTiff uses version 43 instead of TIFF's 42, and header is slightly different, see
|
||||
// http://www.awaresystems.be/imaging/tiff/bigtiff.html
|
||||
int magic = pInput.readUnsignedShort();
|
||||
if (magic != TIFF.TIFF_MAGIC) {
|
||||
throw new IIOException(String.format("Wrong TIFF magic in EXIF data: %04x, expected: %04x", magic, TIFF.TIFF_MAGIC));
|
||||
@@ -72,7 +83,6 @@ public final class EXIFReader extends MetadataReader {
|
||||
|
||||
private EXIFDirectory readDirectory(final ImageInputStream pInput, final long pOffset) throws IOException {
|
||||
List<Entry> entries = new ArrayList<Entry>();
|
||||
|
||||
pInput.seek(pOffset);
|
||||
int entryCount = pInput.readUnsignedShort();
|
||||
|
||||
@@ -82,6 +92,7 @@ public final class EXIFReader extends MetadataReader {
|
||||
|
||||
long nextOffset = pInput.readUnsignedInt();
|
||||
|
||||
// Read linked IFDs
|
||||
if (nextOffset != 0) {
|
||||
EXIFDirectory next = readDirectory(pInput, nextOffset);
|
||||
|
||||
@@ -90,50 +101,158 @@ public final class EXIFReader extends MetadataReader {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Make what sub-IFDs to parse optional? Or leave this to client code? At least skip the non-TIFF data?
|
||||
readSubdirectories(pInput, entries,
|
||||
Arrays.asList(TIFF.TAG_EXIF_IFD, TIFF.TAG_GPS_IFD, TIFF.TAG_INTEROP_IFD
|
||||
// , TIFF.TAG_IPTC, TIFF.TAG_XMP
|
||||
// , TIFF.TAG_ICC_PROFILE
|
||||
// , TIFF.TAG_PHOTOSHOP
|
||||
// ,TIFF.TAG_MODI_OLE_PROPERTY_SET
|
||||
)
|
||||
);
|
||||
|
||||
return new EXIFDirectory(entries);
|
||||
}
|
||||
|
||||
// private Directory readForeignMetadata(final MetadataReader reader, final byte[] bytes) throws IOException {
|
||||
// return reader.read(ImageIO.createImageInputStream(new ByteArrayInputStream(bytes)));
|
||||
// }
|
||||
|
||||
// TODO: Might be better to leave this for client code, as it's tempting go really overboard and support any possible embedded format..
|
||||
private void readSubdirectories(ImageInputStream input, List<Entry> entries, List<Integer> subIFDs) throws IOException {
|
||||
if (subIFDs == null || subIFDs.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0, entriesSize = entries.size(); i < entriesSize; i++) {
|
||||
EXIFEntry entry = (EXIFEntry) entries.get(i);
|
||||
int tagId = (Integer) entry.getIdentifier();
|
||||
|
||||
if (subIFDs.contains(tagId)) {
|
||||
try {
|
||||
Object directory;
|
||||
|
||||
/*
|
||||
if (tagId == TIFF.TAG_IPTC) {
|
||||
directory = readForeignMetadata(new IPTCReader(), (byte[]) entry.getValue());
|
||||
}
|
||||
else if (tagId == TIFF.TAG_XMP) {
|
||||
directory = readForeignMetadata(new XMPReader(), (byte[]) entry.getValue());
|
||||
}
|
||||
else if (tagId == TIFF.TAG_PHOTOSHOP) {
|
||||
// TODO: This is waaay too fragile.. Might need registry-based meta data parsers?
|
||||
try {
|
||||
Class cl = Class.forName("com.twelvemonkeys.imageio.plugins.psd.PSDImageResource");
|
||||
Method method = cl.getMethod("read", ImageInputStream.class);
|
||||
method.setAccessible(true);
|
||||
directory = method.invoke(null, ImageIO.createImageInputStream(new ByteArrayInputStream((byte[]) entry.getValue())));
|
||||
}
|
||||
catch (Exception ignore) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else if (tagId == TIFF.TAG_ICC_PROFILE) {
|
||||
directory = ICC_Profile.getInstance((byte[]) entry.getValue());
|
||||
}
|
||||
else if (tagId == TIFF.TAG_MODI_OLE_PROPERTY_SET) {
|
||||
// TODO: Encapsulate in something more useful?
|
||||
directory = new CompoundDocument(new ByteArrayInputStream((byte[]) entry.getValue())).getRootEntry();
|
||||
}
|
||||
else*/ if (KNOWN_IFDS.contains(tagId)) {
|
||||
directory = readDirectory(input, getPointerOffset(entry));
|
||||
}
|
||||
else {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Replace the entry with parsed data
|
||||
entries.set(i, new EXIFEntry(tagId, directory, entry.getType()));
|
||||
}
|
||||
catch (IIOException e) {
|
||||
// TODO: Issue warning without crashing...?
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private long getPointerOffset(final Entry entry) throws IIOException {
|
||||
long offset;
|
||||
Object value = entry.getValue();
|
||||
|
||||
if (value instanceof Byte) {
|
||||
offset = ((Byte) value & 0xff);
|
||||
}
|
||||
else if (value instanceof Short) {
|
||||
offset = ((Short) value & 0xffff);
|
||||
}
|
||||
else if (value instanceof Integer) {
|
||||
offset = ((Integer) value & 0xffffffffL);
|
||||
}
|
||||
else if (value instanceof Long) {
|
||||
offset = (Long) value;
|
||||
}
|
||||
else {
|
||||
throw new IIOException(String.format("Unknown pointer type: %s", (value != null ? value.getClass() : null)));
|
||||
}
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
private EXIFEntry readEntry(final ImageInputStream pInput) throws IOException {
|
||||
// TODO: BigTiff entries are different
|
||||
int tagId = pInput.readUnsignedShort();
|
||||
|
||||
short type = pInput.readShort();
|
||||
int count = pInput.readInt(); // Number of values
|
||||
|
||||
Object value;
|
||||
if (count < 0) {
|
||||
throw new IIOException(String.format("Illegal count %d for tag %s type %s @%08x", count, tagId, type, pInput.getStreamPosition()));
|
||||
}
|
||||
|
||||
Object value;
|
||||
int valueLength = getValueLength(type, count);
|
||||
|
||||
if (type < 0 || type > 13) {
|
||||
// Invalid tag, this is just for debugging
|
||||
System.err.printf("offset: %08x%n", pInput.getStreamPosition() - 8l);
|
||||
System.err.println("tagId: " + tagId);
|
||||
System.err.println("type: " + type + " (INVALID)");
|
||||
System.err.println("count: " + count);
|
||||
|
||||
if (tagId == TIFF.IFD_EXIF || tagId == TIFF.IFD_GPS || tagId == TIFF.IFD_INTEROP) {
|
||||
// Parse sub IFDs
|
||||
long offset = pInput.readUnsignedInt();
|
||||
pInput.mark();
|
||||
pInput.seek(pInput.getStreamPosition() - 8);
|
||||
|
||||
try {
|
||||
value = readDirectory(pInput, offset);
|
||||
byte[] bytes = new byte[8 + Math.max(20, valueLength)];
|
||||
pInput.readFully(bytes);
|
||||
|
||||
System.err.print("data: " + HexDump.dump(bytes));
|
||||
System.err.println(bytes.length < valueLength ? "..." : "");
|
||||
}
|
||||
finally {
|
||||
pInput.reset();
|
||||
}
|
||||
}
|
||||
else {
|
||||
int valueLength = getValueLength(type, count);
|
||||
|
||||
if (valueLength > 0 && valueLength <= 4) {
|
||||
value = readValueInLine(pInput, type, count);
|
||||
pInput.skipBytes(4 - valueLength);
|
||||
}
|
||||
else {
|
||||
long valueOffset = pInput.readUnsignedInt(); // This is the *value* iff the value size is <= 4 bytes
|
||||
value = readValue(pInput, valueOffset, type, count);
|
||||
}
|
||||
// TODO: For BigTiff allow size <= 8
|
||||
if (valueLength > 0 && valueLength <= 4) {
|
||||
value = readValueInLine(pInput, type, count);
|
||||
pInput.skipBytes(4 - valueLength);
|
||||
}
|
||||
else {
|
||||
long valueOffset = pInput.readUnsignedInt(); // This is the *value* iff the value size is <= 4 bytes
|
||||
value = readValueAt(pInput, valueOffset, type, count);
|
||||
}
|
||||
|
||||
return new EXIFEntry(tagId, value, type);
|
||||
}
|
||||
|
||||
private Object readValue(final ImageInputStream pInput, final long pOffset, final short pType, final int pCount) throws IOException {
|
||||
private Object readValueAt(final ImageInputStream pInput, final long pOffset, final short pType, final int pCount) throws IOException {
|
||||
long pos = pInput.getStreamPosition();
|
||||
try {
|
||||
pInput.seek(pOffset);
|
||||
return readValueInLine(pInput, pType, pCount);
|
||||
return readValue(pInput, pType, pCount);
|
||||
}
|
||||
finally {
|
||||
pInput.seek(pos);
|
||||
@@ -141,53 +260,82 @@ public final class EXIFReader extends MetadataReader {
|
||||
}
|
||||
|
||||
private Object readValueInLine(final ImageInputStream pInput, final short pType, final int pCount) throws IOException {
|
||||
return readValueDirect(pInput, pType, pCount);
|
||||
return readValue(pInput, pType, pCount);
|
||||
}
|
||||
|
||||
private static Object readValueDirect(final ImageInputStream pInput, final short pType, final int pCount) throws IOException {
|
||||
private static Object readValue(final ImageInputStream pInput, final short pType, final int pCount) throws IOException {
|
||||
// TODO: Review value "widening" for the unsigned types. Right now it's inconsistent. Should we leave it to client code?
|
||||
|
||||
long pos = pInput.getStreamPosition();
|
||||
|
||||
switch (pType) {
|
||||
case 2:
|
||||
// TODO: This might be UTF-8 or ISO-8859-1, even though spec says ASCII
|
||||
case 2: // ASCII
|
||||
// TODO: This might be UTF-8 or ISO-8859-x, even though spec says ASCII
|
||||
byte[] ascii = new byte[pCount];
|
||||
pInput.readFully(ascii);
|
||||
return StringUtil.decode(ascii, 0, ascii.length, "UTF-8"); // UTF-8 is ASCII compatible
|
||||
case 1:
|
||||
case 1: // BYTE
|
||||
if (pCount == 1) {
|
||||
return pInput.readUnsignedByte();
|
||||
}
|
||||
case 6:
|
||||
// else fall through
|
||||
case 6: // SBYTE
|
||||
if (pCount == 1) {
|
||||
return pInput.readByte();
|
||||
}
|
||||
case 7:
|
||||
// else fall through
|
||||
case 7: // UNDEFINED
|
||||
byte[] bytes = new byte[pCount];
|
||||
pInput.readFully(bytes);
|
||||
|
||||
// NOTE: We don't change (unsigned) BYTE array wider Java type, as most often BYTE array means
|
||||
// binary data and we want to keep that as a byte array for clients to parse futher
|
||||
|
||||
return bytes;
|
||||
case 3:
|
||||
case 3: // SHORT
|
||||
if (pCount == 1) {
|
||||
return pInput.readUnsignedShort();
|
||||
}
|
||||
case 8:
|
||||
case 8: // SSHORT
|
||||
if (pCount == 1) {
|
||||
return pInput.readShort();
|
||||
}
|
||||
|
||||
short[] shorts = new short[pCount];
|
||||
pInput.readFully(shorts, 0, shorts.length);
|
||||
|
||||
if (pType == 3) {
|
||||
int[] ints = new int[pCount];
|
||||
for (int i = 0; i < pCount; i++) {
|
||||
ints[i] = shorts[i] & 0xffff;
|
||||
}
|
||||
return ints;
|
||||
}
|
||||
|
||||
return shorts;
|
||||
case 4:
|
||||
case 13: // IFD
|
||||
case 4: // LONG
|
||||
if (pCount == 1) {
|
||||
return pInput.readUnsignedInt();
|
||||
}
|
||||
case 9:
|
||||
case 9: // SLONG
|
||||
if (pCount == 1) {
|
||||
return pInput.readInt();
|
||||
}
|
||||
|
||||
int[] ints = new int[pCount];
|
||||
pInput.readFully(ints, 0, ints.length);
|
||||
|
||||
if (pType == 4 || pType == 13) {
|
||||
long[] longs = new long[pCount];
|
||||
for (int i = 0; i < pCount; i++) {
|
||||
longs[i] = ints[i] & 0xffffffffL;
|
||||
}
|
||||
return longs;
|
||||
}
|
||||
|
||||
return ints;
|
||||
case 11:
|
||||
case 11: // FLOAT
|
||||
if (pCount == 1) {
|
||||
return pInput.readFloat();
|
||||
}
|
||||
@@ -195,7 +343,7 @@ public final class EXIFReader extends MetadataReader {
|
||||
float[] floats = new float[pCount];
|
||||
pInput.readFully(floats, 0, floats.length);
|
||||
return floats;
|
||||
case 12:
|
||||
case 12: // DOUBLE
|
||||
if (pCount == 1) {
|
||||
return pInput.readDouble();
|
||||
}
|
||||
@@ -204,7 +352,7 @@ public final class EXIFReader extends MetadataReader {
|
||||
pInput.readFully(doubles, 0, doubles.length);
|
||||
return doubles;
|
||||
|
||||
case 5:
|
||||
case 5: // RATIONAL
|
||||
if (pCount == 1) {
|
||||
return new Rational(pInput.readUnsignedInt(), pInput.readUnsignedInt());
|
||||
}
|
||||
@@ -215,7 +363,7 @@ public final class EXIFReader extends MetadataReader {
|
||||
}
|
||||
|
||||
return rationals;
|
||||
case 10:
|
||||
case 10: // SRATIONAL
|
||||
if (pCount == 1) {
|
||||
return new Rational(pInput.readInt(), pInput.readInt());
|
||||
}
|
||||
@@ -227,8 +375,32 @@ public final class EXIFReader extends MetadataReader {
|
||||
|
||||
return srationals;
|
||||
|
||||
// BigTiff:
|
||||
case 16: // LONG8
|
||||
case 17: // SLONG8
|
||||
case 18: // IFD8
|
||||
// TODO: Assert BigTiff (version == 43)
|
||||
|
||||
if (pCount == 1) {
|
||||
long val = pInput.readLong();
|
||||
if (pType != 17 && val < 0) {
|
||||
throw new IIOException(String.format("Value > %s", Long.MAX_VALUE));
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
long[] longs = new long[pCount];
|
||||
for (int i = 0; i < pCount; i++) {
|
||||
longs[i] = pInput.readLong();
|
||||
}
|
||||
|
||||
return longs;
|
||||
|
||||
default:
|
||||
throw new IIOException(String.format("Unknown EXIF type '%s'", pType));
|
||||
// Spec says skip unknown values:
|
||||
// TODO: Rather just return null, UNKNOWN_TYPE or new Unknown(type, count, offset) for value?
|
||||
return new Unknown(pType, pCount, pos);
|
||||
// throw new IIOException(String.format("Unknown EXIF type '%s' at pos %d", pType, pInput.getStreamPosition()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -239,4 +411,115 @@ public final class EXIFReader extends MetadataReader {
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws IOException {
|
||||
EXIFReader reader = new EXIFReader();
|
||||
ImageInputStream stream = ImageIO.createImageInputStream(new File(args[0]));
|
||||
|
||||
long pos = 0;
|
||||
if (args.length > 1) {
|
||||
if (args[1].startsWith("0x")) {
|
||||
pos = Integer.parseInt(args[1].substring(2), 16);
|
||||
}
|
||||
else {
|
||||
pos = Long.parseLong(args[1]);
|
||||
}
|
||||
|
||||
stream.setByteOrder(pos < 0 ? ByteOrder.LITTLE_ENDIAN : ByteOrder.BIG_ENDIAN);
|
||||
|
||||
pos = Math.abs(pos);
|
||||
|
||||
stream.seek(pos);
|
||||
}
|
||||
|
||||
try {
|
||||
Directory directory;
|
||||
|
||||
if (args.length > 1) {
|
||||
directory = reader.readDirectory(stream, pos);
|
||||
}
|
||||
else {
|
||||
directory = reader.read(stream);
|
||||
}
|
||||
|
||||
for (Entry entry : directory) {
|
||||
System.err.println(entry);
|
||||
|
||||
Object value = entry.getValue();
|
||||
if (value instanceof byte[]) {
|
||||
byte[] bytes = (byte[]) value;
|
||||
System.err.println(HexDump.dump(bytes, 0, Math.min(bytes.length, 128)));
|
||||
}
|
||||
}
|
||||
}
|
||||
finally {
|
||||
stream.close();
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////
|
||||
// TODO: Stream based hex dump util?
|
||||
public static class HexDump {
|
||||
private HexDump() {}
|
||||
|
||||
private static final int WIDTH = 32;
|
||||
|
||||
public static String dump(byte[] bytes) {
|
||||
return dump(bytes, 0, bytes.length);
|
||||
}
|
||||
|
||||
public static String dump(byte[] bytes, int off, int len) {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
|
||||
int i;
|
||||
for (i = 0; i < len; i++) {
|
||||
if (i % WIDTH == 0) {
|
||||
if (i > 0 ) {
|
||||
builder.append("\n");
|
||||
}
|
||||
builder.append(String.format("%08x: ", i + off));
|
||||
}
|
||||
else if (i > 0 && i % 2 == 0) {
|
||||
builder.append(" ");
|
||||
}
|
||||
|
||||
builder.append(String.format("%02x", bytes[i + off]));
|
||||
|
||||
int next = i + 1;
|
||||
if (next % WIDTH == 0 || next == len) {
|
||||
int leftOver = (WIDTH - (next % WIDTH)) % WIDTH;
|
||||
|
||||
if (leftOver != 0) {
|
||||
// Pad: 5 spaces for every 2 bytes... Special care if padding is non-even.
|
||||
int pad = leftOver / 2;
|
||||
|
||||
if (len % 2 != 0) {
|
||||
builder.append(" ");
|
||||
}
|
||||
|
||||
for (int j = 0; j < pad; j++) {
|
||||
builder.append(" ");
|
||||
}
|
||||
}
|
||||
|
||||
builder.append(" ");
|
||||
builder.append(toAsciiString(bytes, next - (WIDTH - leftOver) + off, next + off));
|
||||
}
|
||||
}
|
||||
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
private static String toAsciiString(final byte[] bytes, final int from, final int to) {
|
||||
byte[] range = Arrays.copyOfRange(bytes, from, to);
|
||||
|
||||
for (int i = 0; i < range.length; i++) {
|
||||
if (range[i] < 32 || range[i] > 126) {
|
||||
range[i] = '.'; // Unreadable char
|
||||
}
|
||||
}
|
||||
|
||||
return new String(range, Charset.forName("ascii"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -47,7 +47,7 @@ public interface TIFF {
|
||||
5 = RATIONAL Two LONGs: the first represents the numerator of a
|
||||
fraction; the second, the denominator.
|
||||
|
||||
TIFF 6.0 and above:
|
||||
TIFF 6.0 and above:
|
||||
6 = SBYTE An 8-bit signed (twos-complement) integer.
|
||||
7 = UNDEFINED An 8-bit byte that may contain anything, depending on
|
||||
the definition of the field.
|
||||
@@ -57,21 +57,39 @@ public interface TIFF {
|
||||
fraction, the second the denominator.
|
||||
11 = FLOAT Single precision (4-byte) IEEE format.
|
||||
12 = DOUBLE Double precision (8-byte) IEEE format.
|
||||
|
||||
TODO: Verify IFD type
|
||||
See http://www.awaresystems.be/imaging/tiff/tifftags/subifds.html
|
||||
13 = IFD, same as LONG
|
||||
|
||||
TODO: BigTiff specifies more types
|
||||
See http://www.awaresystems.be/imaging/tiff/bigtiff.html, http://www.remotesensing.org/libtiff/bigtiffdesign.html
|
||||
(what about 14-15??)
|
||||
16 = TIFF_LONG8, being unsigned 8byte integer
|
||||
17 = TIFF_SLONG8, being signed 8byte integer
|
||||
18 = TIFF_IFD8, being a new unsigned 8byte IFD offset.
|
||||
Should probably all map to Java long (and fail if high bit is set for the unsigned types???)
|
||||
*/
|
||||
String[] TYPE_NAMES = {
|
||||
"BYTE", "ASCII", "SHORT", "LONG", "RATIONAL",
|
||||
|
||||
"SBYTE", "UNDEFINED", "SSHORT", "SLONG", "SRATIONAL", "FLOAT", "DOUBLE",
|
||||
"IFD",
|
||||
null, null,
|
||||
"LONG8", "SLONG8", "IFD8"
|
||||
};
|
||||
int[] TYPE_LENGTHS = {
|
||||
1, 1, 2, 4, 8,
|
||||
|
||||
1, 1, 2, 4, 8, 4, 8,
|
||||
4,
|
||||
-1, -1,
|
||||
8, 8, 8
|
||||
};
|
||||
|
||||
int IFD_EXIF = 0x8769;
|
||||
int IFD_GPS = 0x8825;
|
||||
int IFD_INTEROP = 0xA005;
|
||||
/// EXIF defined TIFF tags
|
||||
|
||||
int TAG_EXIF_IFD = 34665;
|
||||
int TAG_GPS_IFD = 34853;
|
||||
int TAG_INTEROP_IFD = 40965;
|
||||
|
||||
/// A. Tags relating to image data structure:
|
||||
|
||||
@@ -114,4 +132,22 @@ public interface TIFF {
|
||||
int TAG_SOFTWARE = 305;
|
||||
int TAG_ARTIST = 315;
|
||||
int TAG_COPYRIGHT = 33432;
|
||||
|
||||
int TAG_SUB_IFD = 330;
|
||||
|
||||
int TAG_XMP = 700;
|
||||
int TAG_IPTC = 33723;
|
||||
int TAG_PHOTOSHOP = 34377;
|
||||
int TAG_ICC_PROFILE = 34675;
|
||||
|
||||
// Microsoft Office Document Imaging (MODI)
|
||||
// http://msdn.microsoft.com/en-us/library/aa167596%28office.11%29.aspx
|
||||
int TAG_MODI_BLC = 34718;
|
||||
int TAG_MODI_VECTOR = 34719;
|
||||
int TAG_MODI_PTC = 34720;
|
||||
|
||||
// http://blogs.msdn.com/b/openspecification/archive/2009/12/08/details-of-three-tiff-tag-extensions-that-microsoft-office-document-imaging-modi-software-may-write-into-the-tiff-files-it-generates.aspx
|
||||
int TAG_MODI_PLAIN_TEXT = 37679;
|
||||
int TAG_MODI_OLE_PROPERTY_SET = 37680;
|
||||
int TAG_MODI_TEXT_POS_INFO = 37681;
|
||||
}
|
||||
|
@@ -0,0 +1,45 @@
|
||||
package com.twelvemonkeys.imageio.metadata.exif;
|
||||
|
||||
/**
|
||||
* Unknown
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @author last modified by $Author: haraldk$
|
||||
* @version $Id: Unknown.java,v 1.0 Oct 8, 2010 3:38:45 PM haraldk Exp$
|
||||
*/
|
||||
final class Unknown {
|
||||
private final short type;
|
||||
private final int count;
|
||||
private final long pos;
|
||||
|
||||
public Unknown(final short type, final int count, final long pos) {
|
||||
this.type = type;
|
||||
this.count = count;
|
||||
this.pos = pos;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return (int) (pos ^ (pos >>> 32)) + count * 37 + type * 97;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if (other != null && other.getClass() == getClass()){
|
||||
Unknown unknown = (Unknown) other;
|
||||
return pos == unknown.pos && type == unknown.type && count == unknown.count;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if (count == 1) {
|
||||
return String.format("Unknown(%d)@%08x", type, pos);
|
||||
}
|
||||
else {
|
||||
return String.format("Unknown(%d)[%d]@%08x", type, count, pos);
|
||||
}
|
||||
}
|
||||
}
|
@@ -49,12 +49,12 @@ public final class JPEGSegmentUtil {
|
||||
|
||||
private JPEGSegmentUtil() {}
|
||||
|
||||
// TODO: Allow for multiple images (multiple SOI markers), using specified index?
|
||||
public static List<Segment> readSegments(final ImageInputStream stream, final int appMarker, final String segmentName) throws IOException {
|
||||
// TODO: Allow for multiple images (multiple SOI markers), using specified index, or document that stream must be placed before SOI of wanted image
|
||||
public static List<Segment> readSegments(final ImageInputStream stream, final int imageIndex, final int appMarker, final String segmentName) throws IOException {
|
||||
return readSegments(stream, Collections.singletonMap(appMarker, Collections.singletonList(segmentName)));
|
||||
}
|
||||
|
||||
public static List<Segment> readSegments(final ImageInputStream stream, Map<Integer, List<String>> segmentIdentifiers) throws IOException {
|
||||
public static List<Segment> readSegments(final ImageInputStream stream, final Map<Integer, List<String>> segmentIdentifiers) throws IOException {
|
||||
readSOI(stream);
|
||||
|
||||
List<Segment> segments = Collections.emptyList();
|
||||
|
@@ -69,7 +69,7 @@ public class XMPScannerTestCase extends TestCase {
|
||||
}
|
||||
|
||||
public void testScanForUTF8singleQuote() throws IOException {
|
||||
InputStream stream = createXMPStream(XMP, "UTF-8".replace("\"", "'"));
|
||||
InputStream stream = createXMPStream(XMP.replace("\"", "'"), "UTF-8");
|
||||
|
||||
Reader reader = XMPScanner.scanForXMPPacket(stream);
|
||||
|
||||
@@ -85,7 +85,7 @@ public class XMPScannerTestCase extends TestCase {
|
||||
}
|
||||
|
||||
public void testScanForUTF16BEsingleQuote() throws IOException {
|
||||
InputStream stream = createXMPStream(XMP, "UTF-16BE".replace("\"", "'"));
|
||||
InputStream stream = createXMPStream(XMP.replace("\"", "'"), "UTF-16BE");
|
||||
|
||||
Reader reader = XMPScanner.scanForXMPPacket(stream);
|
||||
|
||||
@@ -101,7 +101,7 @@ public class XMPScannerTestCase extends TestCase {
|
||||
}
|
||||
|
||||
public void testScanForUTF16LEsingleQuote() throws IOException {
|
||||
InputStream stream = createXMPStream(XMP, "UTF-16LE".replace("\"", "'"));
|
||||
InputStream stream = createXMPStream(XMP.replace("\"", "'"), "UTF-16LE");
|
||||
|
||||
Reader reader = XMPScanner.scanForXMPPacket(stream);
|
||||
|
||||
|
@@ -28,9 +28,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
Parts of this software is based on JVG/JIS.
|
||||
See http://www.cs.hut.fi/~framling/JVG/index.html for more information.
|
||||
Redistribution under BSD authorized by Kary Fr<46>mling:
|
||||
Redistribution under BSD authorized by Kary Fr<46>mling:
|
||||
|
||||
Copyright (c) 2003, Kary Fr<46>mling
|
||||
Copyright (c) 2003, Kary Fr<46>mling
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
@@ -85,7 +85,7 @@ import java.util.List;
|
||||
* <p/>
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @author <a href="http://www.cs.hut.fi/~framling/JVG/">Kary Fr<46>mling</a> (original PICT/QuickDraw parsing)
|
||||
* @author <a href="http://www.cs.hut.fi/~framling/JVG/">Kary Fr<46>mling</a> (original PICT/QuickDraw parsing)
|
||||
* @author <a href="mailto:matthias.wiesmann@a3.epfl.ch">Matthias Wiesmann</a> (original embedded QuickTime parsing)
|
||||
* @version $Id: PICTReader.java,v 1.0 05.apr.2006 15:20:48 haku Exp$
|
||||
*/
|
||||
@@ -151,7 +151,7 @@ public class PICTImageReader extends ImageReaderBase {
|
||||
private Rectangle getPICTFrame() throws IOException {
|
||||
if (mFrame == null) {
|
||||
// Read in header information
|
||||
readPICTHeader(mImageInput);
|
||||
readPICTHeader(imageInput);
|
||||
if (DEBUG) {
|
||||
System.out.println("Done reading PICT header!");
|
||||
}
|
||||
@@ -343,7 +343,7 @@ public class PICTImageReader extends ImageReaderBase {
|
||||
private void drawOnto(Graphics2D pGraphics) throws IOException {
|
||||
mContext = new QuickDrawContext(pGraphics);
|
||||
|
||||
readPICTopcodes(mImageInput);
|
||||
readPICTopcodes(imageInput);
|
||||
if (DEBUG) {
|
||||
System.out.println("Done reading PICT body!");
|
||||
}
|
||||
@@ -1666,7 +1666,7 @@ public class PICTImageReader extends ImageReaderBase {
|
||||
catch (EOFException e) {
|
||||
String pos;
|
||||
try {
|
||||
pos = String.format("position %d", mImageInput.getStreamPosition());
|
||||
pos = String.format("position %d", imageInput.getStreamPosition());
|
||||
}
|
||||
catch (IOException ignore) {
|
||||
pos = "unknown position";
|
||||
@@ -1977,7 +1977,7 @@ public class PICTImageReader extends ImageReaderBase {
|
||||
unPackBits.readFully(pixArray, pixBufOffset, pBounds.width);
|
||||
/*}
|
||||
else {
|
||||
mImageInput.readFully(dstBytes);
|
||||
imageInput.readFully(dstBytes);
|
||||
}*/
|
||||
|
||||
// TODO: Use TYPE_USHORT_555_RGB for 16 bit
|
||||
@@ -2294,7 +2294,7 @@ public class PICTImageReader extends ImageReaderBase {
|
||||
unPackBits.readFully(dstBytes);
|
||||
}
|
||||
else {
|
||||
mImageInput.readFully(dstBytes);
|
||||
imageInput.readFully(dstBytes);
|
||||
}
|
||||
|
||||
if (packType == 3) {
|
||||
|
@@ -28,9 +28,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
Parts of this software is based on JVG/JIS.
|
||||
See http://www.cs.hut.fi/~framling/JVG/index.html for more information.
|
||||
Redistribution under BSD authorized by Kary Fr<46>mling:
|
||||
Redistribution under BSD authorized by Kary Fr<46>mling:
|
||||
|
||||
Copyright (c) 2003, Kary Fr<46>mling
|
||||
Copyright (c) 2003, Kary Fr<46>mling
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
@@ -79,7 +79,7 @@ import java.io.*;
|
||||
* Images are stored using the "opDirectBitsRect" opcode, which directly
|
||||
* stores RGB values (using PackBits run-length encoding).
|
||||
*
|
||||
* @author <a href="http://www.cs.hut.fi/~framling/JVG/">Kary Fr<46>mling</a>
|
||||
* @author <a href="http://www.cs.hut.fi/~framling/JVG/">Kary Fr<46>mling</a>
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @version $Id: PICTWriter.java,v 1.0 05.apr.2006 15:20:48 haku Exp$
|
||||
*/
|
||||
@@ -116,121 +116,121 @@ public class PICTImageWriter extends ImageWriterBase {
|
||||
// TODO: Make 512 byte header optional
|
||||
// Write empty 512-byte header
|
||||
byte[] buf = new byte[PICT.PICT_NULL_HEADER_SIZE];
|
||||
mImageOutput.write(buf);
|
||||
imageOutput.write(buf);
|
||||
|
||||
// Write out the size, leave as 0, this is ok
|
||||
mImageOutput.writeShort(0);
|
||||
imageOutput.writeShort(0);
|
||||
|
||||
// Write image frame (same as image bounds)
|
||||
mImageOutput.writeShort(0);
|
||||
mImageOutput.writeShort(0);
|
||||
mImageOutput.writeShort(pImage.getHeight());
|
||||
mImageOutput.writeShort(pImage.getWidth());
|
||||
imageOutput.writeShort(0);
|
||||
imageOutput.writeShort(0);
|
||||
imageOutput.writeShort(pImage.getHeight());
|
||||
imageOutput.writeShort(pImage.getWidth());
|
||||
|
||||
// Write version, version 2
|
||||
mImageOutput.writeShort(PICT.OP_VERSION);
|
||||
mImageOutput.writeShort(PICT.OP_VERSION_2);
|
||||
imageOutput.writeShort(PICT.OP_VERSION);
|
||||
imageOutput.writeShort(PICT.OP_VERSION_2);
|
||||
|
||||
// Version 2 HEADER_OP, extended version.
|
||||
mImageOutput.writeShort(PICT.OP_HEADER_OP);
|
||||
mImageOutput.writeInt(PICT.HEADER_V2_EXT); // incl 2 bytes reseverd
|
||||
imageOutput.writeShort(PICT.OP_HEADER_OP);
|
||||
imageOutput.writeInt(PICT.HEADER_V2_EXT); // incl 2 bytes reseverd
|
||||
|
||||
// Image resolution, 72 dpi
|
||||
mImageOutput.writeShort(PICT.MAC_DEFAULT_DPI);
|
||||
mImageOutput.writeShort(0);
|
||||
mImageOutput.writeShort(PICT.MAC_DEFAULT_DPI);
|
||||
mImageOutput.writeShort(0);
|
||||
imageOutput.writeShort(PICT.MAC_DEFAULT_DPI);
|
||||
imageOutput.writeShort(0);
|
||||
imageOutput.writeShort(PICT.MAC_DEFAULT_DPI);
|
||||
imageOutput.writeShort(0);
|
||||
|
||||
// Optimal source rectangle (same as image bounds)
|
||||
mImageOutput.writeShort(0);
|
||||
mImageOutput.writeShort(0);
|
||||
mImageOutput.writeShort(pImage.getHeight());
|
||||
mImageOutput.writeShort(pImage.getWidth());
|
||||
imageOutput.writeShort(0);
|
||||
imageOutput.writeShort(0);
|
||||
imageOutput.writeShort(pImage.getHeight());
|
||||
imageOutput.writeShort(pImage.getWidth());
|
||||
|
||||
// Reserved (4 bytes)
|
||||
mImageOutput.writeInt(0);
|
||||
imageOutput.writeInt(0);
|
||||
|
||||
// TODO: The header really ends here...
|
||||
|
||||
// Highlight
|
||||
mImageOutput.writeShort(PICT.OP_DEF_HILITE);
|
||||
imageOutput.writeShort(PICT.OP_DEF_HILITE);
|
||||
|
||||
// Set the clip rectangle
|
||||
mImageOutput.writeShort(PICT.OP_CLIP_RGN);
|
||||
mImageOutput.writeShort(10);
|
||||
mImageOutput.writeShort(0);
|
||||
mImageOutput.writeShort(0);
|
||||
mImageOutput.writeShort(pImage.getHeight());
|
||||
mImageOutput.writeShort(pImage.getWidth());
|
||||
imageOutput.writeShort(PICT.OP_CLIP_RGN);
|
||||
imageOutput.writeShort(10);
|
||||
imageOutput.writeShort(0);
|
||||
imageOutput.writeShort(0);
|
||||
imageOutput.writeShort(pImage.getHeight());
|
||||
imageOutput.writeShort(pImage.getWidth());
|
||||
|
||||
// Pixmap operation
|
||||
mImageOutput.writeShort(PICT.OP_DIRECT_BITS_RECT);
|
||||
imageOutput.writeShort(PICT.OP_DIRECT_BITS_RECT);
|
||||
|
||||
// PixMap pointer (always 0x000000FF);
|
||||
mImageOutput.writeInt(0x000000ff);
|
||||
imageOutput.writeInt(0x000000ff);
|
||||
|
||||
// Write rowBytes, this is 4 times the width.
|
||||
// Set the high bit, to indicate a PixMap.
|
||||
mRowBytes = 4 * pImage.getWidth();
|
||||
mImageOutput.writeShort(0x8000 | mRowBytes);
|
||||
imageOutput.writeShort(0x8000 | mRowBytes);
|
||||
|
||||
// Write bounds rectangle (same as image bounds)
|
||||
mImageOutput.writeShort(0);
|
||||
mImageOutput.writeShort(0);
|
||||
mImageOutput.writeShort(pImage.getHeight()); // TODO: Handle overflow?
|
||||
mImageOutput.writeShort(pImage.getWidth());
|
||||
imageOutput.writeShort(0);
|
||||
imageOutput.writeShort(0);
|
||||
imageOutput.writeShort(pImage.getHeight()); // TODO: Handle overflow?
|
||||
imageOutput.writeShort(pImage.getWidth());
|
||||
|
||||
// PixMap record version
|
||||
mImageOutput.writeShort(0);
|
||||
imageOutput.writeShort(0);
|
||||
|
||||
// Packing format (always 4: PackBits)
|
||||
mImageOutput.writeShort(4);
|
||||
imageOutput.writeShort(4);
|
||||
|
||||
// Size of packed data (leave as 0)
|
||||
mImageOutput.writeInt(0);
|
||||
imageOutput.writeInt(0);
|
||||
|
||||
// Pixmap resolution, 72 dpi
|
||||
mImageOutput.writeShort(PICT.MAC_DEFAULT_DPI);
|
||||
mImageOutput.writeShort(0);
|
||||
mImageOutput.writeShort(PICT.MAC_DEFAULT_DPI);
|
||||
mImageOutput.writeShort(0);
|
||||
imageOutput.writeShort(PICT.MAC_DEFAULT_DPI);
|
||||
imageOutput.writeShort(0);
|
||||
imageOutput.writeShort(PICT.MAC_DEFAULT_DPI);
|
||||
imageOutput.writeShort(0);
|
||||
|
||||
// Pixel type, 16 is allright for direct pixels
|
||||
mImageOutput.writeShort(16);
|
||||
imageOutput.writeShort(16);
|
||||
|
||||
// Pixel size
|
||||
mImageOutput.writeShort(32);
|
||||
imageOutput.writeShort(32);
|
||||
|
||||
// TODO: Allow alpha? Allow 5 bit per pixel component (16 bit)?
|
||||
// Pixel component count
|
||||
mImageOutput.writeShort(3);
|
||||
imageOutput.writeShort(3);
|
||||
|
||||
// Pixel component size
|
||||
mImageOutput.writeShort(8);
|
||||
imageOutput.writeShort(8);
|
||||
|
||||
// PlaneBytes, ignored for now
|
||||
mImageOutput.writeInt(0);
|
||||
imageOutput.writeInt(0);
|
||||
|
||||
// TODO: Allow IndexColorModel?
|
||||
// ColorTable record (for RGB direct pixels, just write 0)
|
||||
mImageOutput.writeInt(0);
|
||||
imageOutput.writeInt(0);
|
||||
|
||||
// Reserved (4 bytes)
|
||||
mImageOutput.writeInt(0);
|
||||
imageOutput.writeInt(0);
|
||||
|
||||
// Source and dest rect (both are same as image bounds)
|
||||
mImageOutput.writeShort(0);
|
||||
mImageOutput.writeShort(0);
|
||||
mImageOutput.writeShort(pImage.getHeight());
|
||||
mImageOutput.writeShort(pImage.getWidth());
|
||||
imageOutput.writeShort(0);
|
||||
imageOutput.writeShort(0);
|
||||
imageOutput.writeShort(pImage.getHeight());
|
||||
imageOutput.writeShort(pImage.getWidth());
|
||||
|
||||
mImageOutput.writeShort(0);
|
||||
mImageOutput.writeShort(0);
|
||||
mImageOutput.writeShort(pImage.getHeight());
|
||||
mImageOutput.writeShort(pImage.getWidth());
|
||||
imageOutput.writeShort(0);
|
||||
imageOutput.writeShort(0);
|
||||
imageOutput.writeShort(pImage.getHeight());
|
||||
imageOutput.writeShort(pImage.getWidth());
|
||||
|
||||
// Transfer mode
|
||||
mImageOutput.writeShort(QuickDraw.SRC_COPY);
|
||||
imageOutput.writeShort(QuickDraw.SRC_COPY);
|
||||
|
||||
// TODO: Move to writePICTData?
|
||||
// TODO: Alpha support
|
||||
@@ -282,13 +282,13 @@ public class PICTImageWriter extends ImageWriterBase {
|
||||
packBits.write(mScanlineBytes);
|
||||
|
||||
if (mRowBytes > 250) {
|
||||
mImageOutput.writeShort(bytes.size());
|
||||
imageOutput.writeShort(bytes.size());
|
||||
}
|
||||
else {
|
||||
mImageOutput.writeByte(bytes.size());
|
||||
imageOutput.writeByte(bytes.size());
|
||||
}
|
||||
|
||||
bytes.writeTo(IIOUtil.createStreamAdapter(mImageOutput));
|
||||
bytes.writeTo(IIOUtil.createStreamAdapter(imageOutput));
|
||||
|
||||
mScanWidthLeft = w;
|
||||
}
|
||||
@@ -327,13 +327,13 @@ public class PICTImageWriter extends ImageWriterBase {
|
||||
packBits.write(mScanlineBytes);
|
||||
|
||||
if (mRowBytes > 250) {
|
||||
mImageOutput.writeShort(bytes.size());
|
||||
imageOutput.writeShort(bytes.size());
|
||||
}
|
||||
else {
|
||||
mImageOutput.writeByte(bytes.size());
|
||||
imageOutput.writeByte(bytes.size());
|
||||
}
|
||||
|
||||
bytes.writeTo(IIOUtil.createStreamAdapter(mImageOutput));
|
||||
bytes.writeTo(IIOUtil.createStreamAdapter(imageOutput));
|
||||
|
||||
mScanWidthLeft = w;
|
||||
}
|
||||
@@ -342,15 +342,15 @@ public class PICTImageWriter extends ImageWriterBase {
|
||||
|
||||
private void writePICTTrailer() throws IOException {
|
||||
// Write out end opcode. Be sure to be word-aligned.
|
||||
long length = mImageOutput.length();
|
||||
long length = imageOutput.length();
|
||||
if (length == -1) {
|
||||
throw new IIOException("Cannot write trailer without knowing length");
|
||||
}
|
||||
|
||||
if ((length & 1) > 0) {
|
||||
mImageOutput.writeByte(0);
|
||||
imageOutput.writeByte(0);
|
||||
}
|
||||
mImageOutput.writeShort(PICT.OP_END_OF_PICTURE);
|
||||
imageOutput.writeShort(PICT.OP_END_OF_PICTURE);
|
||||
}
|
||||
|
||||
public void write(IIOMetadata pStreamMetadata, IIOImage pImage, ImageWriteParam pParam) throws IOException {
|
||||
|
@@ -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;
|
||||
|
@@ -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
|
||||
|
||||
|
@@ -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
|
||||
}
|
||||
}
|
@@ -1,6 +1,7 @@
|
||||
Implement source subsampling and region of interest
|
||||
Separate package for the resources (seems to be a lot)?
|
||||
Possibility to read only some resources? readResources(int[] resourceKeys)?
|
||||
- Probably faster when we only need the color space
|
||||
Support for Photoshop specific TIFF tags (extension for TIFFImageReader)?
|
||||
PSDImageWriter?
|
||||
- Implement source subsampling and region of interest
|
||||
- Separate package for the resources (seems to be a lot)?
|
||||
- Move to metadata package, or implement metadata interface, as this is (similar|equivalent) to TIFF/JPEG APP13 segment tag?
|
||||
- Possibility to read only some resources? readResources(int[] resourceKeys)?
|
||||
- Probably faster when we only need the color space
|
||||
- Support for Photoshop specific TIFF tags (extension for TIFFImageReader)?
|
||||
- PSDImageWriter?
|
||||
|
@@ -209,8 +209,8 @@ public class ThumbsDBImageReader extends ImageReaderBase {
|
||||
@Override
|
||||
public void setInput(Object pInput, boolean pSeekForwardOnly, boolean pIgnoreMetadata) {
|
||||
super.setInput(pInput, pSeekForwardOnly, pIgnoreMetadata);
|
||||
if (mImageInput != null) {
|
||||
mImageInput.setByteOrder(ByteOrder.LITTLE_ENDIAN);
|
||||
if (imageInput != null) {
|
||||
imageInput.setByteOrder(ByteOrder.LITTLE_ENDIAN);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -263,7 +263,7 @@ public class ThumbsDBImageReader extends ImageReaderBase {
|
||||
private void init() throws IOException {
|
||||
assertInput();
|
||||
if (mRoot == null) {
|
||||
mRoot = new CompoundDocument(mImageInput).getRootEntry();
|
||||
mRoot = new CompoundDocument(imageInput).getRootEntry();
|
||||
SortedSet children = mRoot.getChildEntries();
|
||||
|
||||
mThumbnails = new BufferedImage[children.size() - 1];
|
||||
|
Reference in New Issue
Block a user