Mainly new code standard.

A few changes that should have been committed earlier.. :-/
This commit is contained in:
Harald Kuhr
2011-02-17 12:40:49 +01:00
parent 41b8080683
commit 20b87d155d
40 changed files with 951 additions and 593 deletions

View File

@@ -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);
}
}

View File

@@ -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);
}

View File

@@ -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);
}
}
}

View File

@@ -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?

View File

@@ -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;

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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) {
}

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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);

View File

@@ -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);

View File

@@ -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

View File

@@ -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();

View File

@@ -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:

View File

@@ -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++) {

View File

@@ -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 {

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}

View File

@@ -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) {

View File

@@ -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);
}
}

View File

@@ -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);
}

View File

@@ -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
}

View File

@@ -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"));
}
}
}

View File

@@ -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;
}

View File

@@ -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);
}
}
}

View File

@@ -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();

View File

@@ -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);

View File

@@ -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) {

View File

@@ -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 {

View File

@@ -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;

View File

@@ -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

View File

@@ -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
}
}

View File

@@ -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?

View File

@@ -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];